Compare commits

...

No commits in common. "af02378f959717b79ba3a602f900ac71dee7b940" and "88021b2912467e9921a5755cc5efdf9c2d6d1db9" have entirely different histories.

148 changed files with 11249 additions and 1873 deletions

View File

@ -0,0 +1,62 @@
## 目标
- 将 `MqttService` 中解析的三类数据按实体模型入库:
- 单体电压数据 → 表 `subsystem_voltage`
- 单体温度数据 → 表 `subsystem_temperature`
- 累计充/放电量 → 新增表(如 `charge_discharge_summary`
## 现状与模型
- 单体电压解析位于 `src/main/java/com/evobms/project/bms/service/MqttService.java:257-265`,具体方法在 `parseCellVoltageAtIndex``parseCellVoltagesDetail`
- 领域模型:`SubsystemVoltage``src/main/java/com/evobms/project/Battery/domain/SubsystemVoltage.java`
- Mapper/XML`SubsystemVoltageMapper` 与 `mybatis/voltage/SubsystemVoltageMapper.xml`
- 单体温度解析位于 `parseCellTemperatureAtIndex``parseCellTemperaturesDetail`
- 领域模型:`SubsystemTemperature``src/main/java/com/evobms/project/Battery/domain/SubsystemTemperature.java`
- Mapper/XML`SubsystemTemperatureMapper` 与 `mybatis/temperature/SubsystemTemperatureMapper.xml`
- 累计充/放电量解析在 `parseChargeDischargeAtIndex`,目前 `saveChargeDischargeSummary(...)` 空实现,代码库未定义实体与表。
## 字段映射
- 电压(来自 `parseCellVoltageAtIndex`
- `device_id` ⇔ 设备 SN
- `timestamp``new Date()`
- `subsystem_count` ⇔ 索引77
- `subsystem_no` ⇔ 索引78
- `subsystem_voltage` ⇔ 索引79-80 ×0.1V
- `subsystem_current` ⇔ 索引81-82 (值-10000)×0.1A
- `total_battery_count` ⇔ 索引83-84
- `frame_start_battery_no` ⇔ 索引85-86
- `frame_battery_count` ⇔ 索引87
- `battery_voltages` ⇔ 从 `parseCellVoltagesDetail` 得到列表,按 "," 连接为字符串mV→V
- 温度(来自 `parseCellTemperatureAtIndex`
- `device_id`、`timestamp`
- `subsystem_count` ⇔ 索引449
- `subsystem_no` ⇔ 索引450
- `temp_probe_count` ⇔ 索引451-452
- `temperature_values` ⇔ 从 `parseCellTemperaturesDetail` 得到列表(字节-40℃按 "," 连接为字符串
- 充/放电汇总(来自 `parseChargeDischargeAtIndex`
- 建议新增实体 `ChargeDischargeSummary``device_id`, `timestamp`, `discharge_kwh`, `charge_kwh`, 审计字段
## 实施步骤
1. 在 `MqttService` 注入服务
- `ISubsystemVoltageService`、`ISubsystemTemperatureService`
2. 电压数据保存
- 调整 `parseCellVoltagesDetail(byte[], start, deviceSn, startCellIndex, cellCount)` 返回 `List<Double>`(以 V 为单位)
- 在 `parseCellVoltageAtIndex(...)` 构造 `SubsystemVoltage`:填充映射字段与 `battery_voltages`(以 "," 连接),调用 `ISubsystemVoltageService.insertSubsystemVoltage(...)`
3. 温度数据保存
- 调整 `parseCellTemperaturesDetail(byte[], start, deviceSn, tempCount)` 返回 `List<Integer>`(单位 ℃)
- 在 `parseCellTemperatureAtIndex(...)` 构造 `SubsystemTemperature`:填充映射字段与 `temperature_values`(以 "," 连接),调用 `ISubsystemTemperatureService.insertSubsystemTemperature(...)`
4. 累计充/放电量持久化
- 新增实体、Mapper、XML、Service命名示例`ChargeDischargeSummary`、`ChargeDischargeSummaryMapper`、`ChargeDischargeSummaryServiceImpl`
- 在 `saveChargeDischargeSummary(deviceSn, dischargeKwh, chargeKwh)` 构造实体并插入
5. 审计字段
- 为入库对象设置 `create_time`(使用 `BaseEntity.setCreateTime(new Date())``create_by` 设置为设备 SN
## 校验与兼容
- 编译并运行现有单元/集成测试,确保构建成功
- 注意 `SubsystemTemperatureMapper.xml``updatedTime` 属性命名与 `BaseEntity.updateTime` 不一致,当前插入流程不依赖该字段,可后续统一为 `updateTime`
## 变更点位
- `MqttService.java:257-265` 处的三个解析方法内新增调用保存方法
- 新增或调整的私有方法:`saveSubsystemVoltage(...)`、`saveSubsystemTemperature(...)`、完善 `saveChargeDischargeSummary(...)`
- 新实体与持久化文件(仅用于充/放电汇总)按上述命名与目录创建
## 交付结果
- 三类数据均入库,对应后台列表页可查询;极值数据已按此前改造保持一致风格

View File

@ -0,0 +1,8 @@
{
"hash": "764c59fb",
"configHash": "64491d45",
"lockfileHash": "e3b0c442",
"browserHash": "4636522a",
"optimized": {},
"chunks": {}
}

3
.vite/deps/package.json Normal file
View File

@ -0,0 +1,3 @@
{
"type": "module"
}

View File

@ -1,3 +1,4 @@
{
"java.compile.nullAnalysis.mode": "automatic"
"java.compile.nullAnalysis.mode": "automatic",
"java.configuration.updateBuildConfiguration": "interactive"
}

View File

@ -42,53 +42,3 @@
16. 缓存监控:对系统的缓存信息查询,命令统计等。
17. 在线构建器拖动表单元素生成相应的HTML代码。
18. 连接池监视监视当前系统数据库连接池状态可进行分析SQL找出系统性能瓶颈。
## 在线体验
- admin/admin123
- 陆陆续续收到一些打赏,为了更好的体验已用于演示服务器升级。谢谢各位小伙伴。
演示地址http://vue.ruoyi.vip
文档地址http://doc.ruoyi.vip
## 演示图
<table>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/cd1f90be5f2684f4560c9519c0f2a232ee8.jpg"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/1cbcf0e6f257c7d3a063c0e3f2ff989e4b3.jpg"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/up-8074972883b5ba0622e13246738ebba237a.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-9f88719cdfca9af2e58b352a20e23d43b12.png"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/up-39bf2584ec3a529b0d5a3b70d15c9b37646.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-936ec82d1f4872e1bc980927654b6007307.png"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/up-b2d62ceb95d2dd9b3fbe157bb70d26001e9.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-d67451d308b7a79ad6819723396f7c3d77a.png"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/5e8c387724954459291aafd5eb52b456f53.jpg"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/644e78da53c2e92a95dfda4f76e6d117c4b.jpg"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/up-8370a0d02977eebf6dbf854c8450293c937.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-49003ed83f60f633e7153609a53a2b644f7.png"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/up-d4fe726319ece268d4746602c39cffc0621.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-c195234bbcd30be6927f037a6755e6ab69c.png"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/b6115bc8c31de52951982e509930b20684a.jpg"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-5e4daac0bb59612c5038448acbcef235e3a.png"/></td>
</tr>
</table>
## 若依前后端分离交流群
QQ群 [![加入QQ群](https://img.shields.io/badge/已满-937441-blue.svg)](https://jq.qq.com/?_wv=1027&k=5bVB1og) [![加入QQ群](https://img.shields.io/badge/已满-887144332-blue.svg)](https://jq.qq.com/?_wv=1027&k=5eiA4DH) [![加入QQ群](https://img.shields.io/badge/已满-180251782-blue.svg)](https://jq.qq.com/?_wv=1027&k=5AxMKlC) [![加入QQ群](https://img.shields.io/badge/已满-104180207-blue.svg)](https://jq.qq.com/?_wv=1027&k=51G72yr) [![加入QQ群](https://img.shields.io/badge/已满-186866453-blue.svg)](https://jq.qq.com/?_wv=1027&k=VvjN2nvu) [![加入QQ群](https://img.shields.io/badge/已满-201396349-blue.svg)](https://jq.qq.com/?_wv=1027&k=5vYAqA05) [![加入QQ群](https://img.shields.io/badge/已满-101456076-blue.svg)](https://jq.qq.com/?_wv=1027&k=kOIINEb5) [![加入QQ群](https://img.shields.io/badge/已满-101539465-blue.svg)](https://jq.qq.com/?_wv=1027&k=UKtX5jhs) [![加入QQ群](https://img.shields.io/badge/已满-264312783-blue.svg)](https://jq.qq.com/?_wv=1027&k=EI9an8lJ) [![加入QQ群](https://img.shields.io/badge/已满-167385320-blue.svg)](https://jq.qq.com/?_wv=1027&k=SWCtLnMz) [![加入QQ群](https://img.shields.io/badge/已满-104748341-blue.svg)](https://jq.qq.com/?_wv=1027&k=96Dkdq0k) [![加入QQ群](https://img.shields.io/badge/已满-160110482-blue.svg)](https://jq.qq.com/?_wv=1027&k=0fsNiYZt) [![加入QQ群](https://img.shields.io/badge/已满-170801498-blue.svg)](https://jq.qq.com/?_wv=1027&k=7xw4xUG1) [![加入QQ群](https://img.shields.io/badge/已满-108482800-blue.svg)](https://jq.qq.com/?_wv=1027&k=eCx8eyoJ) [![加入QQ群](https://img.shields.io/badge/已满-101046199-blue.svg)](https://jq.qq.com/?_wv=1027&k=SpyH2875) [![加入QQ群](https://img.shields.io/badge/已满-136919097-blue.svg)](https://jq.qq.com/?_wv=1027&k=tKEt51dz) [![加入QQ群](https://img.shields.io/badge/已满-143961921-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=0vBbSb0ztbBgVtn3kJS-Q4HUNYwip89G&authKey=8irq5PhutrZmWIvsUsklBxhj57l%2F1nOZqjzigkXZVoZE451GG4JHPOqW7AW6cf0T&noverify=0&group_code=143961921) [![加入QQ群](https://img.shields.io/badge/已满-174951577-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=ZFAPAbp09S2ltvwrJzp7wGlbopsc0rwi&authKey=HB2cxpxP2yspk%2Bo3WKTBfktRCccVkU26cgi5B16u0KcAYrVu7sBaE7XSEqmMdFQp&noverify=0&group_code=174951577) [![加入QQ群](https://img.shields.io/badge/已满-161281055-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Fn2aF5IHpwsy8j6VlalNJK6qbwFLFHat&authKey=uyIT%2B97x2AXj3odyXpsSpVaPMC%2Bidw0LxG5MAtEqlrcBcWJUA%2FeS43rsF1Tg7IRJ&noverify=0&group_code=161281055) [![加入QQ群](https://img.shields.io/badge/已满-138988063-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=XIzkm_mV2xTsUtFxo63bmicYoDBA6Ifm&authKey=dDW%2F4qsmw3x9govoZY9w%2FoWAoC4wbHqGal%2BbqLzoS6VBarU8EBptIgPKN%2FviyC8j&noverify=0&group_code=138988063) [![加入QQ群](https://img.shields.io/badge/已满-151450850-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=DkugnCg68PevlycJSKSwjhFqfIgrWWwR&authKey=pR1Pa5lPIeGF%2FFtIk6d%2FGB5qFi0EdvyErtpQXULzo03zbhopBHLWcuqdpwY241R%2F&noverify=0&group_code=151450850) [![加入QQ群](https://img.shields.io/badge/已满-224622315-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=F58bgRa-Dp-rsQJThiJqIYv8t4-lWfXh&authKey=UmUs4CVG5OPA1whvsa4uSespOvyd8%2FAr9olEGaWAfdLmfKQk%2FVBp2YU3u2xXXt76&noverify=0&group_code=224622315) [![加入QQ群](https://img.shields.io/badge/已满-287842588-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Nxb2EQ5qozWa218Wbs7zgBnjLSNk_tVT&authKey=obBKXj6SBKgrFTJZx0AqQnIYbNOvBB2kmgwWvGhzxR67RoRr84%2Bus5OadzMcdJl5&noverify=0&group_code=287842588) [![加入QQ群](https://img.shields.io/badge/已满-187944233-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=numtK1M_I4eVd2Gvg8qtbuL8JgX42qNh&authKey=giV9XWMaFZTY%2FqPlmWbkB9g3fi0Ev5CwEtT9Tgei0oUlFFCQLDp4ozWRiVIzubIm&noverify=0&group_code=187944233) [![加入QQ群](https://img.shields.io/badge/已满-228578329-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=G6r5KGCaa3pqdbUSXNIgYloyb8e0_L0D&authKey=4w8tF1eGW7%2FedWn%2FHAypQksdrML%2BDHolQSx7094Agm7Luakj9EbfPnSTxSi2T1LQ&noverify=0&group_code=228578329) [![加入QQ群](https://img.shields.io/badge/已满-191164766-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=GsOo-OLz53J8y_9TPoO6XXSGNRTgbFxA&authKey=R7Uy%2Feq%2BZsoKNqHvRKhiXpypW7DAogoWapOawUGHokJSBIBIre2%2FoiAZeZBSLuBc&noverify=0&group_code=191164766) [![加入QQ群](https://img.shields.io/badge/174569686-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=PmYavuzsOthVqfdAPbo4uAeIbu7Ttjgc&authKey=p52l8%2FXa4PS1JcEmS3VccKSwOPJUZ1ZfQ69MEKzbrooNUljRtlKjvsXf04bxNp3G&noverify=0&group_code=174569686) 点击按钮入群。

Binary file not shown.

138
ota/1.1.1.bin Normal file
View File

@ -0,0 +1,138 @@
```
String finalNickName = currentUserNickName;
CompletableFuture.runAsync(() -> {
try {
if (uploadFile != null && uploadFile.exists()) {
String planUserIds = dingTalkProperties.getPlanUserId();
if (StringUtils.isNotBlank(planUserIds)) {
String mediaId = mediaService.uploadMedia("file", uploadFile.getAbsolutePath());
// 支持多人推送,假设多个 userId 以逗号分隔
String[] userIds = planUserIds.split(",");
for (String userId : userIds) {
String cleanUserId = userId.trim();
if (StringUtils.isNotBlank(cleanUserId)) {
try {
robotGroupService.sendFileToUser(cleanUserId, mediaId, originalFilename);
log.info("上传的工艺计划表已成功发送给用户: {}", cleanUserId);
} catch (Exception e) {
log.error("发送文件给用户 {} 异常", cleanUserId, e);
}
}
}
} else {
log.warn("未配置钉钉工艺计划接收人(planUserId),跳过发送文件");
}
}
} catch (Exception e) {
log.error("发送文件给工艺计划接收人异常", e);
} finally {
if (uploadFile != null && uploadFile.exists()) {
uploadFile.delete();
}
}
```
```
/**
* 发送文件给个人
*
* @param userId 接收人userId
* @param mediaId 媒体文件ID
* @param fileName 文件名
* @return success
*/
public String sendFileToUser(String userId, String mediaId, String fileName) {
try {
Config config = new Config();
config.protocol = "https";
config.regionId = "central";
Client client = new Client(config);
BatchSendOTOHeaders headers = new BatchSendOTOHeaders();
headers.xAcsDingtalkAccessToken = tokenManager.getAccessToken();
String msgParam = String.format("{\"mediaId\":\"%s\",\"fileName\":\"%s\"}",
mediaId, fileName);
BatchSendOTORequest request = new BatchSendOTORequest()
.setRobotCode(properties.getRobotCode())
.setUserIds(Arrays.asList(userId))
.setMsgKey("sampleFile")
.setMsgParam(msgParam);
client.batchSendOTOWithOptions(request, headers, new com.aliyun.teautil.models.RuntimeOptions());
log.info("机器人个人文件发送成功, userId: {}, fileName: {}", userId, fileName);
return "success";
} catch (Exception e) {
log.error("机器人个人文件发送失败", e);
throw new DingException("机器人个人文件发送失败", e);
}
}
```
```
/**
* 发送Markdown消息给个人 (批量单聊)
*
* @param userId 接收人userId
* @param title 标题
* @param text Markdown内容
*/
public void sendMarkdownToUser(String userId, String title, String text) {
try {
Config config = new Config();
config.protocol = "https";
config.regionId = "central";
Client client = new Client(config);
BatchSendOTOHeaders headers = new BatchSendOTOHeaders();
headers.xAcsDingtalkAccessToken = tokenManager.getAccessToken();
String msgParam = String.format("{\"title\":\"%s\",\"text\":\"%s\"}",
title.replace("\"", "\\\""),
text.replace("\"", "\\\"").replace("\n", "\\n"));
BatchSendOTORequest request = new BatchSendOTORequest()
.setRobotCode(properties.getRobotCode())
.setUserIds(Collections.singletonList(userId))
.setMsgKey("sampleMarkdown")
.setMsgParam(msgParam);
client.batchSendOTOWithOptions(request, headers, new RuntimeOptions());
log.info("机器人个人Markdown消息发送成功, userId: {}, title: {}", userId, title);
} catch (Exception e) {
log.error("机器人个人Markdown消息发送失败", e);
throw new DingException("机器人个人Markdown消息发送失败", e);
}
}
```
```
/**
* 刷新AccessToken
*/
private void refresh() {
try {
Config config = new Config();
config.protocol = "https";
config.regionId = "central";
Client client = new Client(config);
GetAccessTokenRequest request = new GetAccessTokenRequest()
.setAppKey(properties.getAppKey())
.setAppSecret(properties.getAppSecret());
GetAccessTokenResponse response = client.getAccessToken(request);
if (response.getBody() != null) {
this.accessToken = response.getBody().getAccessToken();
// 设置过期时间提前100秒刷新
this.expireTime = System.currentTimeMillis() + (response.getBody().getExpireIn() * 1000) - 100000;
log.info("钉钉AccessToken刷新成功过期时间: {}", this.expireTime);
} else {
throw new DingException("刷新AccessToken失败返回为空");
}
} catch (Exception e) {
log.error("刷新钉钉AccessToken失败", e);
throw new DingException("刷新钉钉AccessToken失败: " + e.getMessage(), e);
}
}
```

138
ota/2.2.0.bin Normal file
View File

@ -0,0 +1,138 @@
```
String finalNickName = currentUserNickName;
CompletableFuture.runAsync(() -> {
try {
if (uploadFile != null && uploadFile.exists()) {
String planUserIds = dingTalkProperties.getPlanUserId();
if (StringUtils.isNotBlank(planUserIds)) {
String mediaId = mediaService.uploadMedia("file", uploadFile.getAbsolutePath());
// 支持多人推送,假设多个 userId 以逗号分隔
String[] userIds = planUserIds.split(",");
for (String userId : userIds) {
String cleanUserId = userId.trim();
if (StringUtils.isNotBlank(cleanUserId)) {
try {
robotGroupService.sendFileToUser(cleanUserId, mediaId, originalFilename);
log.info("上传的工艺计划表已成功发送给用户: {}", cleanUserId);
} catch (Exception e) {
log.error("发送文件给用户 {} 异常", cleanUserId, e);
}
}
}
} else {
log.warn("未配置钉钉工艺计划接收人(planUserId),跳过发送文件");
}
}
} catch (Exception e) {
log.error("发送文件给工艺计划接收人异常", e);
} finally {
if (uploadFile != null && uploadFile.exists()) {
uploadFile.delete();
}
}
```
```
/**
* 发送文件给个人
*
* @param userId 接收人userId
* @param mediaId 媒体文件ID
* @param fileName 文件名
* @return success
*/
public String sendFileToUser(String userId, String mediaId, String fileName) {
try {
Config config = new Config();
config.protocol = "https";
config.regionId = "central";
Client client = new Client(config);
BatchSendOTOHeaders headers = new BatchSendOTOHeaders();
headers.xAcsDingtalkAccessToken = tokenManager.getAccessToken();
String msgParam = String.format("{\"mediaId\":\"%s\",\"fileName\":\"%s\"}",
mediaId, fileName);
BatchSendOTORequest request = new BatchSendOTORequest()
.setRobotCode(properties.getRobotCode())
.setUserIds(Arrays.asList(userId))
.setMsgKey("sampleFile")
.setMsgParam(msgParam);
client.batchSendOTOWithOptions(request, headers, new com.aliyun.teautil.models.RuntimeOptions());
log.info("机器人个人文件发送成功, userId: {}, fileName: {}", userId, fileName);
return "success";
} catch (Exception e) {
log.error("机器人个人文件发送失败", e);
throw new DingException("机器人个人文件发送失败", e);
}
}
```
```
/**
* 发送Markdown消息给个人 (批量单聊)
*
* @param userId 接收人userId
* @param title 标题
* @param text Markdown内容
*/
public void sendMarkdownToUser(String userId, String title, String text) {
try {
Config config = new Config();
config.protocol = "https";
config.regionId = "central";
Client client = new Client(config);
BatchSendOTOHeaders headers = new BatchSendOTOHeaders();
headers.xAcsDingtalkAccessToken = tokenManager.getAccessToken();
String msgParam = String.format("{\"title\":\"%s\",\"text\":\"%s\"}",
title.replace("\"", "\\\""),
text.replace("\"", "\\\"").replace("\n", "\\n"));
BatchSendOTORequest request = new BatchSendOTORequest()
.setRobotCode(properties.getRobotCode())
.setUserIds(Collections.singletonList(userId))
.setMsgKey("sampleMarkdown")
.setMsgParam(msgParam);
client.batchSendOTOWithOptions(request, headers, new RuntimeOptions());
log.info("机器人个人Markdown消息发送成功, userId: {}, title: {}", userId, title);
} catch (Exception e) {
log.error("机器人个人Markdown消息发送失败", e);
throw new DingException("机器人个人Markdown消息发送失败", e);
}
}
```
```
/**
* 刷新AccessToken
*/
private void refresh() {
try {
Config config = new Config();
config.protocol = "https";
config.regionId = "central";
Client client = new Client(config);
GetAccessTokenRequest request = new GetAccessTokenRequest()
.setAppKey(properties.getAppKey())
.setAppSecret(properties.getAppSecret());
GetAccessTokenResponse response = client.getAccessToken(request);
if (response.getBody() != null) {
this.accessToken = response.getBody().getAccessToken();
// 设置过期时间提前100秒刷新
this.expireTime = System.currentTimeMillis() + (response.getBody().getExpireIn() * 1000) - 100000;
log.info("钉钉AccessToken刷新成功过期时间: {}", this.expireTime);
} else {
throw new DingException("刷新AccessToken失败返回为空");
}
} catch (Exception e) {
log.error("刷新钉钉AccessToken失败", e);
throw new DingException("刷新钉钉AccessToken失败: " + e.getMessage(), e);
}
}
```

27
pom.xml
View File

@ -22,7 +22,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<java.version>16</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<pagehelper.spring.boot.starter.version>1.4.7</pagehelper.spring.boot.starter.version>
<fastjson.version>2.0.58</fastjson.version>
@ -30,6 +30,7 @@
<commons.io.version>2.19.0</commons.io.version>
<bitwalker.version>1.21</bitwalker.version>
<jwt.version>0.9.1</jwt.version>
<mysql.version>8.0.33</mysql.version>
<kaptcha.version>2.3.3</kaptcha.version>
<swagger.version>3.0.0</swagger.version>
<poi.version>4.1.2</poi.version>
@ -44,12 +45,24 @@
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- SpringBoot 核心包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<!-- SpringBoot 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
@ -96,9 +109,10 @@
<!-- Mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.version}</version>
<!-- <scope>runtime</scope>-->
</dependency>
<!-- pagehelper 分页插件 -->
@ -270,7 +284,6 @@
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
@ -287,8 +300,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>14</source>
<target>14</target>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>

View File

@ -15,6 +15,7 @@ public final class BboxApiConstants {
public static final int CODE_DEVICE_NOT_REGISTERED = 901; // 设备未在平台注册如平台未录入
public static final int CODE_DEVICE_DISABLED = 902; // 设备已被禁用
public static final int CODE_FILE_NOT_FOUND = 903; // 文件不存在
public static final int CODE_PARAM_INVALID = 904; // 参数无效/缺失
// 对应消息文本可用于统一提示
public static final String MSG_SUCCESS = "成功";
@ -23,4 +24,5 @@ public final class BboxApiConstants {
public static final String MSG_DEVICE_NOT_REGISTERED = "设备未在平台注册,如平台未录入";
public static final String MSG_DEVICE_DISABLED = "设备已被禁用";
public static final String MSG_FILE_NOT_FOUND = "文件不存在";
}
public static final String MSG_PARAM_INVALID = "参数无效";
}

View File

@ -12,17 +12,61 @@ import com.evobms.framework.web.page.TableSupport;
*/
public class PageUtils extends PageHelper
{
/** 设备历史平铺列表允许的每页条数 */
public static final int[] HISTORY_PAGE_SIZES = {10, 20, 100, 500};
/**
* 设置请求分页数据
*/
public static void startPage()
{
startPage(true);
}
/**
* @param doCount 是否执行 COUNT大数据量历史列表可传 false 避免扫行计数超时
*/
public static void startPage(boolean doCount)
{
PageDomain pageDomain = TableSupport.buildPageRequest();
Integer pageNum = pageDomain.getPageNum();
Integer pageSize = pageDomain.getPageSize();
String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
Boolean reasonable = pageDomain.getReasonable();
PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
PageHelper.startPage(pageNum, pageSize, doCount, null, null)
.setOrderBy(orderBy)
.setReasonable(reasonable);
}
/**
* 历史平铺对时间范围内全部 vehicle_data 做库内分页pageSize 规范为 10/20/100/500
*
* @return 实际使用的每页条数
*/
public static int startHistoryPage(boolean doCount) {
PageDomain pageDomain = TableSupport.buildPageRequest();
int pageNum = pageDomain.getPageNum() == null || pageDomain.getPageNum() < 1 ? 1 : pageDomain.getPageNum();
int pageSize = normalizeHistoryPageSize(pageDomain.getPageSize());
String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
Boolean reasonable = pageDomain.getReasonable();
PageHelper.startPage(pageNum, pageSize, doCount, null, null)
.setOrderBy(orderBy)
.setReasonable(reasonable);
return pageSize;
}
/** 将请求的 pageSize 收敛到 10、20、100、500不足 10 按 10 */
public static int normalizeHistoryPageSize(Integer requested) {
if (requested == null || requested <= 0) {
return HISTORY_PAGE_SIZES[0];
}
int chosen = HISTORY_PAGE_SIZES[0];
for (int allowed : HISTORY_PAGE_SIZES) {
if (requested >= allowed) {
chosen = allowed;
}
}
return chosen;
}
/**

View File

@ -1,13 +1,14 @@
package com.evobms.framework.aspectj.lang.annotation;
import com.evobms.common.utils.poi.ExcelHandlerAdapter;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.math.BigDecimal;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import com.evobms.common.utils.poi.ExcelHandlerAdapter;
/**
* 自定义导出Excel数据注解

View File

@ -10,7 +10,9 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.ExecutorChannel;
import org.springframework.integration.core.MessageProducer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.integration.handler.AbstractMessageHandler;
import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
@ -60,6 +62,9 @@ public class MqttConfig {
@Value("${mqtt.cleanSession}")
private boolean cleanSession;
@Value("${mqtt.completionTimeout:20000}")
private int completionTimeout;
// 支持逗号分隔的多个主题例如/bbox/EVO0002/#,/bbox/000010000200003/#
@Value("#{'${mqtt.subscribeTopic}'.split(',')}")
private String[] subscribeTopics;
@ -87,11 +92,17 @@ public class MqttConfig {
}
/**
* MQTT消息接收通道
* MQTT消息接收通道 (异步处理防止入库慢阻塞MQTT底层网络线程导致掉线)
*/
@Bean
public MessageChannel mqttInputChannel() {
return new DirectChannel();
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(1000);
executor.setThreadNamePrefix("mqtt-handler-");
executor.initialize();
return new ExecutorChannel(executor);
}
/**
@ -108,7 +119,7 @@ public class MqttConfig {
MqttPahoMessageDrivenChannelAdapter adapter =
new MqttPahoMessageDrivenChannelAdapter(clientId + "_inbound", mqttClientFactory(), topics);
adapter.setCompletionTimeout(5000);
adapter.setCompletionTimeout(completionTimeout);
// 使用字节载荷转换器保留原始二进制帧
DefaultPahoMessageConverter converter = new DefaultPahoMessageConverter();
converter.setPayloadAsBytes(true);
@ -150,7 +161,6 @@ public class MqttConfig {
// 尽最大可能保留原始字节ISO_8859_1一一映射
payloadBytes = ((String) payloadObj).getBytes(StandardCharsets.ISO_8859_1);
} else {
// 兜底从toString获取再按ISO_8859_1转换
payloadBytes = payloadObj.toString().getBytes(StandardCharsets.ISO_8859_1);
}

View File

@ -8,8 +8,8 @@ import java.util.List;
import javax.sql.DataSource;
import org.apache.ibatis.io.VFS;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -122,11 +122,11 @@ public class MyBatisConfig
typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
VFS.addImplClass(SpringBootVFS.class);
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));
sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
return sessionFactory.getObject();
}
}
}

View File

@ -114,8 +114,15 @@ public class SecurityConfig
requests.antMatchers("/login", "/register", "/captchaImage").permitAll()
// 静态资源可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
.antMatchers("/swagger-ui.html", "/swagger-ui/**", "/swagger-resources/**", "/webjars/**", "/v3/api-docs/**", "/*/api-docs", "/druid/**", "/doc.html", "/doc.html/**", "/knife4j/**").permitAll()
.antMatchers("/dev-api/swagger-ui/**", "/dev-api/swagger-resources/**", "/dev-api/v3/api-docs/**", "/dev-api/doc.html", "/dev-api/knife4j/**").permitAll()
.antMatchers("/devices/**","/ota/**").permitAll()
.antMatchers("/battery-data/**").permitAll()
.antMatchers("/vehicle-data/**").permitAll()
.antMatchers("/vehicle-location/**").permitAll()
.antMatchers("/subsystem-voltage/**").permitAll()
.antMatchers("/subsystem-temperature/**").permitAll()
.antMatchers("/extreme-values/**").permitAll()
.antMatchers("/iot/**").permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();

View File

@ -23,7 +23,7 @@ import springfox.documentation.spring.web.plugins.Docket;
/**
* Swagger2的接口配置
*
*
* @author ruoyi
*/
@Configuration
@ -54,11 +54,7 @@ public class SwaggerConfig
.apiInfo(apiInfo())
// 设置哪些接口暴露给Swagger展示
.select()
// 扫描所有有注解的api用这种方式更灵活
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
// 扫描指定包中的swagger注解
// .apis(RequestHandlerSelectors.basePackage("com.evobms.project.tool.swagger"))
// 扫描所有 .apis(RequestHandlerSelectors.any())
.apis(RequestHandlerSelectors.basePackage("com.evobms.project"))
.paths(PathSelectors.any())
.build()
/* 设置安全模式swagger可以设置访问token */
@ -112,9 +108,9 @@ public class SwaggerConfig
// 用ApiInfoBuilder进行定制
return new ApiInfoBuilder()
// 设置标题
.title("标题:若依管理系统_接口文档")
.title("标题:EVO-BMS 电池管理系统_接口文档")
// 描述
.description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...")
.description("")
// 作者信息
.contact(new Contact(ruoyiConfig.getName(), null, null))
// 版本

View File

@ -4,13 +4,15 @@ import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModelProperty;
/**
* Entity基类
*
*
* @author ruoyi
*/
public class BaseEntity implements Serializable
@ -19,27 +21,37 @@ public class BaseEntity implements Serializable
/** 搜索值 */
@JsonIgnore
@TableField(exist = false)
@ApiModelProperty(value = "搜索值")
private String searchValue;
/** 创建者 */
@ApiModelProperty(value = "创建者")
private String createBy;
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "创建时间")
private Date createTime;
/** 更新者 */
@ApiModelProperty(value = "更新者")
private String updateBy;
/** 更新时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "更新时间")
private Date updateTime;
/** 备注 */
@TableField(exist = false)
@ApiModelProperty(value = "备注")
private String remark;
/** 请求参数 */
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@ApiModelProperty(value = "请求参数")
@TableField(exist = false)
private Map<String, Object> params;
public String getSearchValue()

View File

@ -1,4 +1,4 @@
package com.evobms.project.Battery.controller;
package com.evobms.project.battery.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
@ -14,21 +14,24 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.evobms.framework.aspectj.lang.annotation.Log;
import com.evobms.framework.aspectj.lang.enums.BusinessType;
import com.evobms.project.Battery.domain.ExtremeValues;
import com.evobms.project.Battery.service.IExtremeValuesService;
import com.evobms.project.battery.domain.ExtremeValues;
import com.evobms.project.battery.service.IExtremeValuesService;
import com.evobms.framework.web.controller.BaseController;
import com.evobms.framework.web.domain.AjaxResult;
import com.evobms.common.utils.poi.ExcelUtil;
import com.evobms.framework.web.page.TableDataInfo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
/**
* 电池极值数据Controller
*
*
* @author 田志阳
* @date 2025-10-14
* @date 2025-11-15
*/
@RestController
@RequestMapping("/BatteryData/BatteryData")
@RequestMapping("/Battery/ExtremeValues")
@Api(tags = "电池极值数据")
public class ExtremeValuesController extends BaseController
{
@Autowired
@ -37,8 +40,9 @@ public class ExtremeValuesController extends BaseController
/**
* 查询电池极值数据列表
*/
@PreAuthorize("@ss.hasPermi('BatteryData:BatteryData:list')")
@PreAuthorize("@ss.hasPermi('Battery:ExtremeValues:list')")
@GetMapping("/list")
@ApiOperation("查询电池极值数据列表")
public TableDataInfo list(ExtremeValues extremeValues)
{
startPage();
@ -49,9 +53,10 @@ public class ExtremeValuesController extends BaseController
/**
* 导出电池极值数据列表
*/
@PreAuthorize("@ss.hasPermi('BatteryData:BatteryData:export')")
@PreAuthorize("@ss.hasPermi('Battery:ExtremeValues:export')")
@Log(title = "电池极值数据", businessType = BusinessType.EXPORT)
@PostMapping("/export")
@ApiOperation("导出电池极值数据列表")
public void export(HttpServletResponse response, ExtremeValues extremeValues)
{
List<ExtremeValues> list = extremeValuesService.selectExtremeValuesList(extremeValues);
@ -62,8 +67,9 @@ public class ExtremeValuesController extends BaseController
/**
* 获取电池极值数据详细信息
*/
@PreAuthorize("@ss.hasPermi('BatteryData:BatteryData:query')")
@PreAuthorize("@ss.hasPermi('Battery:ExtremeValues:query')")
@GetMapping(value = "/{id}")
@ApiOperation("获取电池极值数据详细信息")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(extremeValuesService.selectExtremeValuesById(id));
@ -72,9 +78,10 @@ public class ExtremeValuesController extends BaseController
/**
* 新增电池极值数据
*/
@PreAuthorize("@ss.hasPermi('BatteryData:BatteryData:add')")
@PreAuthorize("@ss.hasPermi('Battery:ExtremeValues:add')")
@Log(title = "电池极值数据", businessType = BusinessType.INSERT)
@PostMapping
@ApiOperation("新增电池极值数据")
public AjaxResult add(@RequestBody ExtremeValues extremeValues)
{
return toAjax(extremeValuesService.insertExtremeValues(extremeValues));
@ -83,9 +90,10 @@ public class ExtremeValuesController extends BaseController
/**
* 修改电池极值数据
*/
@PreAuthorize("@ss.hasPermi('BatteryData:BatteryData:edit')")
@PreAuthorize("@ss.hasPermi('Battery:ExtremeValues:edit')")
@Log(title = "电池极值数据", businessType = BusinessType.UPDATE)
@PutMapping
@ApiOperation("修改电池极值数据")
public AjaxResult edit(@RequestBody ExtremeValues extremeValues)
{
return toAjax(extremeValuesService.updateExtremeValues(extremeValues));
@ -94,9 +102,10 @@ public class ExtremeValuesController extends BaseController
/**
* 删除电池极值数据
*/
@PreAuthorize("@ss.hasPermi('BatteryData:BatteryData:remove')")
@PreAuthorize("@ss.hasPermi('Battery:ExtremeValues:remove')")
@Log(title = "电池极值数据", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
@DeleteMapping("/{ids}")
@ApiOperation("删除电池极值数据")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(extremeValuesService.deleteExtremeValuesByIds(ids));

View File

@ -0,0 +1,113 @@
package com.evobms.project.battery.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.evobms.framework.aspectj.lang.annotation.Log;
import com.evobms.framework.aspectj.lang.enums.BusinessType;
import com.evobms.project.battery.domain.SubsystemTemperature;
import com.evobms.project.battery.service.ISubsystemTemperatureService;
import com.evobms.framework.web.controller.BaseController;
import com.evobms.framework.web.domain.AjaxResult;
import com.evobms.common.utils.poi.ExcelUtil;
import com.evobms.framework.web.page.TableDataInfo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
/**
* 可充电储能子系统温度信息Controller
*
* @author 田志阳
* @date 2025-11-15
*/
@RestController
@RequestMapping("/Battery/SubsystemTemperature")
@Api(tags = "可充电储能子系统温度信息")
public class SubsystemTemperatureController extends BaseController
{
@Autowired
private ISubsystemTemperatureService subsystemTemperatureService;
/**
* 查询可充电储能子系统温度信息列表
*/
@PreAuthorize("@ss.hasPermi('Battery:SubsystemTemperature:list')")
@GetMapping("/list")
@ApiOperation("查询子系统温度信息列表")
public TableDataInfo list(SubsystemTemperature subsystemTemperature)
{
startPage();
List<SubsystemTemperature> list = subsystemTemperatureService.selectSubsystemTemperatureList(subsystemTemperature);
return getDataTable(list);
}
/**
* 导出可充电储能子系统温度信息列表
*/
@PreAuthorize("@ss.hasPermi('Battery:SubsystemTemperature:export')")
@Log(title = "可充电储能子系统温度信息", businessType = BusinessType.EXPORT)
@PostMapping("/export")
@ApiOperation("导出子系统温度信息列表")
public void export(HttpServletResponse response, SubsystemTemperature subsystemTemperature)
{
List<SubsystemTemperature> list = subsystemTemperatureService.selectSubsystemTemperatureList(subsystemTemperature);
ExcelUtil<SubsystemTemperature> util = new ExcelUtil<SubsystemTemperature>(SubsystemTemperature.class);
util.exportExcel(response, list, "可充电储能子系统温度信息数据");
}
/**
* 获取可充电储能子系统温度信息详细信息
*/
@PreAuthorize("@ss.hasPermi('Battery:SubsystemTemperature:query')")
@GetMapping(value = "/{id}")
@ApiOperation("获取子系统温度信息详细信息")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(subsystemTemperatureService.selectSubsystemTemperatureById(id));
}
/**
* 新增可充电储能子系统温度信息
*/
@PreAuthorize("@ss.hasPermi('Battery:SubsystemTemperature:add')")
@Log(title = "可充电储能子系统温度信息", businessType = BusinessType.INSERT)
@PostMapping
@ApiOperation("新增子系统温度信息")
public AjaxResult add(@RequestBody SubsystemTemperature subsystemTemperature)
{
return toAjax(subsystemTemperatureService.insertSubsystemTemperature(subsystemTemperature));
}
/**
* 修改可充电储能子系统温度信息
*/
@PreAuthorize("@ss.hasPermi('Battery:SubsystemTemperature:edit')")
@Log(title = "可充电储能子系统温度信息", businessType = BusinessType.UPDATE)
@PutMapping
@ApiOperation("修改子系统温度信息")
public AjaxResult edit(@RequestBody SubsystemTemperature subsystemTemperature)
{
return toAjax(subsystemTemperatureService.updateSubsystemTemperature(subsystemTemperature));
}
/**
* 删除可充电储能子系统温度信息
*/
@PreAuthorize("@ss.hasPermi('Battery:SubsystemTemperature:remove')")
@Log(title = "可充电储能子系统温度信息", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
@ApiOperation("删除子系统温度信息")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(subsystemTemperatureService.deleteSubsystemTemperatureByIds(ids));
}
}

View File

@ -0,0 +1,113 @@
package com.evobms.project.battery.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.evobms.framework.aspectj.lang.annotation.Log;
import com.evobms.framework.aspectj.lang.enums.BusinessType;
import com.evobms.project.battery.domain.SubsystemVoltage;
import com.evobms.project.battery.service.ISubsystemVoltageService;
import com.evobms.framework.web.controller.BaseController;
import com.evobms.framework.web.domain.AjaxResult;
import com.evobms.common.utils.poi.ExcelUtil;
import com.evobms.framework.web.page.TableDataInfo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
/**
* 可充电储能子系统电压信息Controller
*
* @author 田志阳
* @date 2025-11-15
*/
@RestController
@RequestMapping("/Battery/SubsystemVoltage")
@Api(tags = "可充电储能子系统电压信息")
public class SubsystemVoltageController extends BaseController
{
@Autowired
private ISubsystemVoltageService subsystemVoltageService;
/**
* 查询可充电储能子系统电压信息列表
*/
@PreAuthorize("@ss.hasPermi('Battery:SubsystemVoltage:list')")
@GetMapping("/list")
@ApiOperation("查询子系统电压信息列表")
public TableDataInfo list(SubsystemVoltage subsystemVoltage)
{
startPage();
List<SubsystemVoltage> list = subsystemVoltageService.selectSubsystemVoltageList(subsystemVoltage);
return getDataTable(list);
}
/**
* 导出可充电储能子系统电压信息列表
*/
@PreAuthorize("@ss.hasPermi('Battery:SubsystemVoltage:export')")
@Log(title = "可充电储能子系统电压信息", businessType = BusinessType.EXPORT)
@PostMapping("/export")
@ApiOperation("导出子系统电压信息列表")
public void export(HttpServletResponse response, SubsystemVoltage subsystemVoltage)
{
List<SubsystemVoltage> list = subsystemVoltageService.selectSubsystemVoltageList(subsystemVoltage);
ExcelUtil<SubsystemVoltage> util = new ExcelUtil<SubsystemVoltage>(SubsystemVoltage.class);
util.exportExcel(response, list, "可充电储能子系统电压信息数据");
}
/**
* 获取可充电储能子系统电压信息详细信息
*/
@PreAuthorize("@ss.hasPermi('Battery:SubsystemVoltage:query')")
@GetMapping(value = "/{id}")
@ApiOperation("获取子系统电压信息详细信息")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(subsystemVoltageService.selectSubsystemVoltageById(id));
}
/**
* 新增可充电储能子系统电压信息
*/
@PreAuthorize("@ss.hasPermi('Battery:SubsystemVoltage:add')")
@Log(title = "可充电储能子系统电压信息", businessType = BusinessType.INSERT)
@PostMapping
@ApiOperation("新增子系统电压信息")
public AjaxResult add(@RequestBody SubsystemVoltage subsystemVoltage)
{
return toAjax(subsystemVoltageService.insertSubsystemVoltage(subsystemVoltage));
}
/**
* 修改可充电储能子系统电压信息
*/
@PreAuthorize("@ss.hasPermi('Battery:SubsystemVoltage:edit')")
@Log(title = "可充电储能子系统电压信息", businessType = BusinessType.UPDATE)
@PutMapping
@ApiOperation("修改子系统电压信息")
public AjaxResult edit(@RequestBody SubsystemVoltage subsystemVoltage)
{
return toAjax(subsystemVoltageService.updateSubsystemVoltage(subsystemVoltage));
}
/**
* 删除可充电储能子系统电压信息
*/
@PreAuthorize("@ss.hasPermi('Battery:SubsystemVoltage:remove')")
@Log(title = "可充电储能子系统电压信息", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
@ApiOperation("删除子系统电压信息")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(subsystemVoltageService.deleteSubsystemVoltageByIds(ids));
}
}

View File

@ -1,8 +1,12 @@
package com.evobms.project.Battery.domain;
package com.evobms.project.battery.domain;
import java.math.BigDecimal;
import java.util.Date;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.evobms.framework.aspectj.lang.annotation.Excel;
@ -10,220 +14,238 @@ import com.evobms.framework.web.domain.BaseEntity;
/**
* 电池极值数据对象 extreme_values
*
*
* @author 田志阳
* @date 2025-10-14
*/
@ApiModel(description = "电池极值数据对象")
@Data
@EqualsAndHashCode(callSuper = false)
public class ExtremeValues extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键ID */
@ApiModelProperty(value = "主键ID")
private Long id;
/** 设备ID */
@ApiModelProperty(value = "设备ID")
@Excel(name = "设备ID")
private String deviceId;
/** 数据时间戳 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "数据时间戳", width = 30, dateFormat = "yyyy-MM-dd")
@ApiModelProperty(value = "数据时间戳")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "数据时间戳", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date timestamp;
/** 最高电压电池子系统号 */
@ApiModelProperty(value = "最高电压电池子系统号")
@Excel(name = "最高电压电池子系统号")
private Integer maxVoltageSubsystemNo;
/** 最高电压电池单体代号 */
@ApiModelProperty(value = "最高电压电池单体代号")
@Excel(name = "最高电压电池单体代号")
private Integer maxVoltageBatteryNo;
/** 电池单体电压最高值(V) */
@ApiModelProperty(value = "电池单体电压最高值(V)")
@Excel(name = "电池单体电压最高值(V)")
private BigDecimal maxVoltageValue;
/** 最低电压电池子系统号 */
@ApiModelProperty(value = "最低电压电池子系统号")
@Excel(name = "最低电压电池子系统号")
private Integer minVoltageSubsystemNo;
/** 最低电压电池单体代号 */
@ApiModelProperty(value = "最低电压电池单体代号")
@Excel(name = "最低电压电池单体代号")
private Integer minVoltageBatteryNo;
/** 电池单体电压最低值(V) */
@ApiModelProperty(value = "电池单体电压最低值(V)")
@Excel(name = "电池单体电压最低值(V)")
private BigDecimal minVoltageValue;
/** 最高温度子系统号 */
@ApiModelProperty(value = "最高温度子系统号")
@Excel(name = "最高温度子系统号")
private Integer maxTempSubsystemNo;
/** 最高温度探针序号 */
@ApiModelProperty(value = "最高温度探针序号")
@Excel(name = "最高温度探针序号")
private Integer maxTempProbeNo;
/** 最高温度值(℃) */
@ApiModelProperty(value = "最高温度值(℃)")
@Excel(name = "最高温度值(℃)")
private BigDecimal maxTempValue;
/** 最低温度子系统号 */
@ApiModelProperty(value = "最低温度子系统号")
@Excel(name = "最低温度子系统号")
private Integer minTempSubsystemNo;
/** 最低温度探针序号 */
@ApiModelProperty(value = "最低温度探针序号")
@Excel(name = "最低温度探针序号")
private Integer minTempProbeNo;
/** 最低温度值(℃) */
@ApiModelProperty(value = "最低温度值(℃)")
@Excel(name = "最低温度值(℃)")
private BigDecimal minTempValue;
public void setId(Long id)
public void setId(Long id)
{
this.id = id;
}
public Long getId()
public Long getId()
{
return id;
}
public void setDeviceId(String deviceId)
public void setDeviceId(String deviceId)
{
this.deviceId = deviceId;
}
public String getDeviceId()
public String getDeviceId()
{
return deviceId;
}
public void setTimestamp(Date timestamp)
public void setTimestamp(Date timestamp)
{
this.timestamp = timestamp;
}
public Date getTimestamp()
public Date getTimestamp()
{
return timestamp;
}
public void setMaxVoltageSubsystemNo(Integer maxVoltageSubsystemNo)
public void setMaxVoltageSubsystemNo(Integer maxVoltageSubsystemNo)
{
this.maxVoltageSubsystemNo = maxVoltageSubsystemNo;
}
public Integer getMaxVoltageSubsystemNo()
public Integer getMaxVoltageSubsystemNo()
{
return maxVoltageSubsystemNo;
}
public void setMaxVoltageBatteryNo(Integer maxVoltageBatteryNo)
public void setMaxVoltageBatteryNo(Integer maxVoltageBatteryNo)
{
this.maxVoltageBatteryNo = maxVoltageBatteryNo;
}
public Integer getMaxVoltageBatteryNo()
public Integer getMaxVoltageBatteryNo()
{
return maxVoltageBatteryNo;
}
public void setMaxVoltageValue(BigDecimal maxVoltageValue)
public void setMaxVoltageValue(BigDecimal maxVoltageValue)
{
this.maxVoltageValue = maxVoltageValue;
}
public BigDecimal getMaxVoltageValue()
public BigDecimal getMaxVoltageValue()
{
return maxVoltageValue;
}
public void setMinVoltageSubsystemNo(Integer minVoltageSubsystemNo)
public void setMinVoltageSubsystemNo(Integer minVoltageSubsystemNo)
{
this.minVoltageSubsystemNo = minVoltageSubsystemNo;
}
public Integer getMinVoltageSubsystemNo()
public Integer getMinVoltageSubsystemNo()
{
return minVoltageSubsystemNo;
}
public void setMinVoltageBatteryNo(Integer minVoltageBatteryNo)
public void setMinVoltageBatteryNo(Integer minVoltageBatteryNo)
{
this.minVoltageBatteryNo = minVoltageBatteryNo;
}
public Integer getMinVoltageBatteryNo()
public Integer getMinVoltageBatteryNo()
{
return minVoltageBatteryNo;
}
public void setMinVoltageValue(BigDecimal minVoltageValue)
public void setMinVoltageValue(BigDecimal minVoltageValue)
{
this.minVoltageValue = minVoltageValue;
}
public BigDecimal getMinVoltageValue()
public BigDecimal getMinVoltageValue()
{
return minVoltageValue;
}
public void setMaxTempSubsystemNo(Integer maxTempSubsystemNo)
public void setMaxTempSubsystemNo(Integer maxTempSubsystemNo)
{
this.maxTempSubsystemNo = maxTempSubsystemNo;
}
public Integer getMaxTempSubsystemNo()
public Integer getMaxTempSubsystemNo()
{
return maxTempSubsystemNo;
}
public void setMaxTempProbeNo(Integer maxTempProbeNo)
public void setMaxTempProbeNo(Integer maxTempProbeNo)
{
this.maxTempProbeNo = maxTempProbeNo;
}
public Integer getMaxTempProbeNo()
public Integer getMaxTempProbeNo()
{
return maxTempProbeNo;
}
public void setMaxTempValue(BigDecimal maxTempValue)
public void setMaxTempValue(BigDecimal maxTempValue)
{
this.maxTempValue = maxTempValue;
}
public BigDecimal getMaxTempValue()
public BigDecimal getMaxTempValue()
{
return maxTempValue;
}
public void setMinTempSubsystemNo(Integer minTempSubsystemNo)
public void setMinTempSubsystemNo(Integer minTempSubsystemNo)
{
this.minTempSubsystemNo = minTempSubsystemNo;
}
public Integer getMinTempSubsystemNo()
public Integer getMinTempSubsystemNo()
{
return minTempSubsystemNo;
}
public void setMinTempProbeNo(Integer minTempProbeNo)
public void setMinTempProbeNo(Integer minTempProbeNo)
{
this.minTempProbeNo = minTempProbeNo;
}
public Integer getMinTempProbeNo()
public Integer getMinTempProbeNo()
{
return minTempProbeNo;
}
public void setMinTempValue(BigDecimal minTempValue)
public void setMinTempValue(BigDecimal minTempValue)
{
this.minTempValue = minTempValue;
}
public BigDecimal getMinTempValue()
public BigDecimal getMinTempValue()
{
return minTempValue;
}

View File

@ -0,0 +1,72 @@
package com.evobms.project.battery.domain;
import java.util.Date;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import com.evobms.framework.aspectj.lang.annotation.Excel;
import com.evobms.framework.web.domain.BaseEntity;
import lombok.EqualsAndHashCode;
/**
* 可充电储能子系统温度信息对象 subsystem_temperature
*
* @author 田志阳
* @date 2025-11-15
*/
@Data
@ApiModel(description = "可充电储能子系统温度信息对象")
@EqualsAndHashCode(callSuper = false)
public class SubsystemTemperature extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键ID */
@ApiModelProperty(value = "主键ID")
private Long id;
/** 设备ID */
@ApiModelProperty(value = "设备ID")
@Excel(name = "设备ID")
private String deviceId;
/** 数据时间戳 */
@ApiModelProperty(value = "数据时间戳")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "数据时间戳", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date timestamp;
/** 可充电储能子系统个数 */
@ApiModelProperty(value = "可充电储能子系统个数")
@Excel(name = "可充电储能子系统个数")
private Integer subsystemCount;
/** 可充电储能子系统号 */
@ApiModelProperty(value = "可充电储能子系统号")
@Excel(name = "可充电储能子系统号")
private Integer subsystemNo;
/** 可充电储能温度探针个数 */
@ApiModelProperty(value = "可充电储能温度探针个数")
@Excel(name = "可充电储能温度探针个数")
private Integer tempProbeCount;
/** 各温度探针检测的温度值数组(℃) */
@ApiModelProperty(value = "各温度探针检测的温度值数组(℃)")
@Excel(name = "各温度探针检测的温度值数组(℃)")
private String temperatureValues;
/** 创建时间 */
@ApiModelProperty(value = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date createTime;
/** 更新时间 */
@ApiModelProperty(value = "更新时间")
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "更新时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date updateTime;
}

View File

@ -0,0 +1,93 @@
package com.evobms.project.battery.domain;
import java.math.BigDecimal;
import java.util.Date;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.evobms.framework.aspectj.lang.annotation.Excel;
import com.evobms.framework.web.domain.BaseEntity;
/**
* 可充电储能子系统电压信息对象 subsystem_voltage
*
* @author 田志阳
* @date 2025-11-15
*/
@Data
@ApiModel(description = "可充电储能子系统电压信息对象")
public class SubsystemVoltage extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键ID */
@ApiModelProperty(value = "主键ID")
private Long id;
/** 设备ID */
@ApiModelProperty(value = "设备ID")
@Excel(name = "设备ID")
private String deviceId;
/** 数据时间戳 */
@ApiModelProperty(value = "数据时间戳")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "数据时间戳", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date timestamp;
/** 可充电储能子系统个数 */
@ApiModelProperty(value = "可充电储能子系统个数")
@Excel(name = "可充电储能子系统个数")
private Integer subsystemCount;
/** 可充电储能子系统号 */
@ApiModelProperty(value = "可充电储能子系统号")
@Excel(name = "可充电储能子系统号")
private Integer subsystemNo;
/** 可充电储能装置电压(V) */
@ApiModelProperty(value = "可充电储能装置电压(V)")
@Excel(name = "可充电储能装置电压(V)")
private BigDecimal subsystemVoltage;
/** 可充电储能装置电流(A) */
@ApiModelProperty(value = "可充电储能装置电流(A)")
@Excel(name = "可充电储能装置电流(A)")
private BigDecimal subsystemCurrent;
/** 单体电池总数 */
@ApiModelProperty(value = "单体电池总数")
@Excel(name = "单体电池总数")
private Integer totalBatteryCount;
/** 本帧起始电池序号 */
@ApiModelProperty(value = "本帧起始电池序号")
@Excel(name = "本帧起始电池序号")
private Integer frameStartBatteryNo;
/** 本帧单体电池总数 */
@ApiModelProperty(value = "本帧单体电池总数")
@Excel(name = "本帧单体电池总数")
private Integer frameBatteryCount;
/** 单体电池电压数组(V) */
@ApiModelProperty(value = "单体电池电压数组(V)")
@Excel(name = "单体电池电压数组(V)")
private String batteryVoltages;
/** 创建时间 */
@ApiModelProperty(value = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date createTime;
/** 更新时间 */
@ApiModelProperty(value = "更新时间")
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "更新时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date updateTime;
}

View File

@ -1,19 +1,22 @@
package com.evobms.project.Battery.mapper;
package com.evobms.project.battery.mapper;
import java.util.List;
import com.evobms.project.Battery.domain.ExtremeValues;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.evobms.project.battery.domain.ExtremeValues;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
/**
* 电池极值数据Mapper接口
*
*
* @author 田志阳
* @date 2025-10-14
*/
public interface ExtremeValuesMapper
{
public interface ExtremeValuesMapper extends BaseMapper<ExtremeValues> {
/**
* 查询电池极值数据
*
*
* @param id 电池极值数据主键
* @return 电池极值数据
*/
@ -21,7 +24,7 @@ public interface ExtremeValuesMapper
/**
* 查询电池极值数据列表
*
*
* @param extremeValues 电池极值数据
* @return 电池极值数据集合
*/
@ -29,7 +32,7 @@ public interface ExtremeValuesMapper
/**
* 新增电池极值数据
*
*
* @param extremeValues 电池极值数据
* @return 结果
*/
@ -37,7 +40,7 @@ public interface ExtremeValuesMapper
/**
* 修改电池极值数据
*
*
* @param extremeValues 电池极值数据
* @return 结果
*/
@ -45,7 +48,7 @@ public interface ExtremeValuesMapper
/**
* 删除电池极值数据
*
*
* @param id 电池极值数据主键
* @return 结果
*/
@ -53,9 +56,13 @@ public interface ExtremeValuesMapper
/**
* 批量删除电池极值数据
*
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteExtremeValuesByIds(Long[] ids);
ExtremeValues selectLatestByDeviceId(@Param("deviceId") String deviceId);
ExtremeValues selectLatestByDeviceIdBefore(@Param("deviceId") String deviceId, @Param("beforeTime") Date beforeTime);
}

View File

@ -0,0 +1,65 @@
package com.evobms.project.battery.mapper;
import java.util.List;
import com.evobms.project.battery.domain.SubsystemTemperature;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
/**
* 可充电储能子系统温度信息Mapper接口
*
* @author 田志阳
* @date 2025-11-15
*/
public interface SubsystemTemperatureMapper extends BaseMapper<SubsystemTemperature>
{
/**
* 查询可充电储能子系统温度信息
*
* @param id 可充电储能子系统温度信息主键
* @return 可充电储能子系统温度信息
*/
public SubsystemTemperature selectSubsystemTemperatureById(Long id);
/**
* 查询可充电储能子系统温度信息列表
*
* @param subsystemTemperature 可充电储能子系统温度信息
* @return 可充电储能子系统温度信息集合
*/
public List<SubsystemTemperature> selectSubsystemTemperatureList(SubsystemTemperature subsystemTemperature);
/**
* 新增可充电储能子系统温度信息
*
* @param subsystemTemperature 可充电储能子系统温度信息
* @return 结果
*/
public int insertSubsystemTemperature(SubsystemTemperature subsystemTemperature);
/**
* 修改可充电储能子系统温度信息
*
* @param subsystemTemperature 可充电储能子系统温度信息
* @return 结果
*/
public int updateSubsystemTemperature(SubsystemTemperature subsystemTemperature);
/**
* 删除可充电储能子系统温度信息
*
* @param id 可充电储能子系统温度信息主键
* @return 结果
*/
public int deleteSubsystemTemperatureById(Long id);
/**
* 批量删除可充电储能子系统温度信息
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteSubsystemTemperatureByIds(Long[] ids);
SubsystemTemperature selectLatestByDeviceAndSubsystem(@Param("deviceId") String deviceId, @Param("subsystemNo") Integer subsystemNo);
}

View File

@ -0,0 +1,65 @@
package com.evobms.project.battery.mapper;
import java.util.List;
import com.evobms.project.battery.domain.SubsystemVoltage;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
/**
* 可充电储能子系统电压信息Mapper接口
*
* @author 田志阳
* @date 2025-11-15
*/
public interface SubsystemVoltageMapper extends BaseMapper<SubsystemVoltage>
{
/**
* 查询可充电储能子系统电压信息
*
* @param id 可充电储能子系统电压信息主键
* @return 可充电储能子系统电压信息
*/
public SubsystemVoltage selectSubsystemVoltageById(Long id);
/**
* 查询可充电储能子系统电压信息列表
*
* @param subsystemVoltage 可充电储能子系统电压信息
* @return 可充电储能子系统电压信息集合
*/
public List<SubsystemVoltage> selectSubsystemVoltageList(SubsystemVoltage subsystemVoltage);
/**
* 新增可充电储能子系统电压信息
*
* @param subsystemVoltage 可充电储能子系统电压信息
* @return 结果
*/
public int insertSubsystemVoltage(SubsystemVoltage subsystemVoltage);
/**
* 修改可充电储能子系统电压信息
*
* @param subsystemVoltage 可充电储能子系统电压信息
* @return 结果
*/
public int updateSubsystemVoltage(SubsystemVoltage subsystemVoltage);
/**
* 删除可充电储能子系统电压信息
*
* @param id 可充电储能子系统电压信息主键
* @return 结果
*/
public int deleteSubsystemVoltageById(Long id);
/**
* 批量删除可充电储能子系统电压信息
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteSubsystemVoltageByIds(Long[] ids);
SubsystemVoltage selectLatestByDeviceAndSubsystem(@Param("deviceId") String deviceId, @Param("subsystemNo") Integer subsystemNo);
}

View File

@ -1,19 +1,19 @@
package com.evobms.project.Battery.service;
package com.evobms.project.battery.service;
import java.util.List;
import com.evobms.project.Battery.domain.ExtremeValues;
import com.evobms.project.battery.domain.ExtremeValues;
/**
* 电池极值数据Service接口
*
*
* @author 田志阳
* @date 2025-10-14
*/
public interface IExtremeValuesService
public interface IExtremeValuesService
{
/**
* 查询电池极值数据
*
*
* @param id 电池极值数据主键
* @return 电池极值数据
*/
@ -21,7 +21,7 @@ public interface IExtremeValuesService
/**
* 查询电池极值数据列表
*
*
* @param extremeValues 电池极值数据
* @return 电池极值数据集合
*/
@ -29,7 +29,7 @@ public interface IExtremeValuesService
/**
* 新增电池极值数据
*
*
* @param extremeValues 电池极值数据
* @return 结果
*/
@ -37,7 +37,7 @@ public interface IExtremeValuesService
/**
* 修改电池极值数据
*
*
* @param extremeValues 电池极值数据
* @return 结果
*/
@ -45,7 +45,7 @@ public interface IExtremeValuesService
/**
* 批量删除电池极值数据
*
*
* @param ids 需要删除的电池极值数据主键集合
* @return 结果
*/
@ -53,9 +53,13 @@ public interface IExtremeValuesService
/**
* 删除电池极值数据信息
*
*
* @param id 电池极值数据主键
* @return 结果
*/
public int deleteExtremeValuesById(Long id);
ExtremeValues selectLatestByDeviceId(String deviceId);
ExtremeValues selectLatestByDeviceIdBefore(String deviceId, java.util.Date beforeTime);
}

View File

@ -0,0 +1,64 @@
package com.evobms.project.battery.service;
import java.util.List;
import com.baomidou.mybatisplus.extension.service.IService;
import com.evobms.project.battery.domain.SubsystemTemperature;
/**
* 可充电储能子系统温度信息Service接口
*
* @author 田志阳
* @date 2025-11-15
*/
public interface ISubsystemTemperatureService extends IService<SubsystemTemperature>
{
/**
* 查询可充电储能子系统温度信息
*
* @param id 可充电储能子系统温度信息主键
* @return 可充电储能子系统温度信息
*/
public SubsystemTemperature selectSubsystemTemperatureById(Long id);
/**
* 查询可充电储能子系统温度信息列表
*
* @param subsystemTemperature 可充电储能子系统温度信息
* @return 可充电储能子系统温度信息集合
*/
public List<SubsystemTemperature> selectSubsystemTemperatureList(SubsystemTemperature subsystemTemperature);
/**
* 新增可充电储能子系统温度信息
*
* @param subsystemTemperature 可充电储能子系统温度信息
* @return 结果
*/
public int insertSubsystemTemperature(SubsystemTemperature subsystemTemperature);
/**
* 修改可充电储能子系统温度信息
*
* @param subsystemTemperature 可充电储能子系统温度信息
* @return 结果
*/
public int updateSubsystemTemperature(SubsystemTemperature subsystemTemperature);
/**
* 批量删除可充电储能子系统温度信息
*
* @param ids 需要删除的可充电储能子系统温度信息主键集合
* @return 结果
*/
public int deleteSubsystemTemperatureByIds(Long[] ids);
/**
* 删除可充电储能子系统温度信息信息
*
* @param id 可充电储能子系统温度信息主键
* @return 结果
*/
public int deleteSubsystemTemperatureById(Long id);
SubsystemTemperature selectLatestByDeviceAndSubsystem(String deviceId, Integer subsystemNo);
}

View File

@ -0,0 +1,64 @@
package com.evobms.project.battery.service;
import java.util.List;
import com.baomidou.mybatisplus.extension.service.IService;
import com.evobms.project.battery.domain.SubsystemVoltage;
/**
* 可充电储能子系统电压信息Service接口
*
* @author 田志阳
* @date 2025-11-15
*/
public interface ISubsystemVoltageService extends IService<SubsystemVoltage>
{
/**
* 查询可充电储能子系统电压信息
*
* @param id 可充电储能子系统电压信息主键
* @return 可充电储能子系统电压信息
*/
public SubsystemVoltage selectSubsystemVoltageById(Long id);
/**
* 查询可充电储能子系统电压信息列表
*
* @param subsystemVoltage 可充电储能子系统电压信息
* @return 可充电储能子系统电压信息集合
*/
public List<SubsystemVoltage> selectSubsystemVoltageList(SubsystemVoltage subsystemVoltage);
/**
* 新增可充电储能子系统电压信息
*
* @param subsystemVoltage 可充电储能子系统电压信息
* @return 结果
*/
public int insertSubsystemVoltage(SubsystemVoltage subsystemVoltage);
/**
* 修改可充电储能子系统电压信息
*
* @param subsystemVoltage 可充电储能子系统电压信息
* @return 结果
*/
public int updateSubsystemVoltage(SubsystemVoltage subsystemVoltage);
/**
* 批量删除可充电储能子系统电压信息
*
* @param ids 需要删除的可充电储能子系统电压信息主键集合
* @return 结果
*/
public int deleteSubsystemVoltageByIds(Long[] ids);
/**
* 删除可充电储能子系统电压信息信息
*
* @param id 可充电储能子系统电压信息主键
* @return 结果
*/
public int deleteSubsystemVoltageById(Long id);
SubsystemVoltage selectLatestByDeviceAndSubsystem(String deviceId, Integer subsystemNo);
}

View File

@ -1,28 +1,30 @@
package com.evobms.project.Battery.service.impl;
package com.evobms.project.battery.service.impl;
import java.util.List;
import com.evobms.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.evobms.project.Battery.mapper.ExtremeValuesMapper;
import com.evobms.project.Battery.domain.ExtremeValues;
import com.evobms.project.Battery.service.IExtremeValuesService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evobms.project.battery.mapper.ExtremeValuesMapper;
import com.evobms.project.battery.domain.ExtremeValues;
import com.evobms.project.battery.service.IExtremeValuesService;
/**
* 电池极值数据Service业务层处理
*
*
* @author 田志阳
* @date 2025-10-14
*/
@Service
public class ExtremeValuesServiceImpl implements IExtremeValuesService
public class ExtremeValuesServiceImpl extends ServiceImpl<ExtremeValuesMapper, ExtremeValues> implements IExtremeValuesService
{
@Autowired
private ExtremeValuesMapper extremeValuesMapper;
/**
* 查询电池极值数据
*
*
* @param id 电池极值数据主键
* @return 电池极值数据
*/
@ -34,19 +36,40 @@ public class ExtremeValuesServiceImpl implements IExtremeValuesService
/**
* 查询电池极值数据列表
*
*
* @param extremeValues 电池极值数据
* @return 电池极值数据
*/
@Override
public List<ExtremeValues> selectExtremeValuesList(ExtremeValues extremeValues)
{
return extremeValuesMapper.selectExtremeValuesList(extremeValues);
LambdaQueryWrapper<ExtremeValues> qw = new LambdaQueryWrapper<>();
if (extremeValues != null) {
if (extremeValues.getDeviceId() != null && !extremeValues.getDeviceId().isEmpty()) {
qw.eq(ExtremeValues::getDeviceId, extremeValues.getDeviceId());
}
if (extremeValues.getTimestamp() != null) {
qw.ge(ExtremeValues::getTimestamp, extremeValues.getTimestamp());
}
java.util.Map<String, Object> params = extremeValues.getParams();
if (params != null) {
java.util.Date begin = com.evobms.common.utils.DateUtils.parseDate(params.get("beginTime"));
java.util.Date end = com.evobms.common.utils.DateUtils.parseDate(params.get("endTime"));
if (begin != null) {
qw.ge(ExtremeValues::getTimestamp, begin);
}
if (end != null) {
qw.le(ExtremeValues::getTimestamp, end);
}
}
}
qw.orderByDesc(ExtremeValues::getTimestamp);
return extremeValuesMapper.selectList(qw);
}
/**
* 新增电池极值数据
*
*
* @param extremeValues 电池极值数据
* @return 结果
*/
@ -59,7 +82,7 @@ public class ExtremeValuesServiceImpl implements IExtremeValuesService
/**
* 修改电池极值数据
*
*
* @param extremeValues 电池极值数据
* @return 结果
*/
@ -72,7 +95,7 @@ public class ExtremeValuesServiceImpl implements IExtremeValuesService
/**
* 批量删除电池极值数据
*
*
* @param ids 需要删除的电池极值数据主键
* @return 结果
*/
@ -84,7 +107,7 @@ public class ExtremeValuesServiceImpl implements IExtremeValuesService
/**
* 删除电池极值数据信息
*
*
* @param id 电池极值数据主键
* @return 结果
*/
@ -93,4 +116,14 @@ public class ExtremeValuesServiceImpl implements IExtremeValuesService
{
return extremeValuesMapper.deleteExtremeValuesById(id);
}
@Override
public ExtremeValues selectLatestByDeviceId(String deviceId) {
return extremeValuesMapper.selectLatestByDeviceId(deviceId);
}
@Override
public ExtremeValues selectLatestByDeviceIdBefore(String deviceId, java.util.Date beforeTime) {
return extremeValuesMapper.selectLatestByDeviceIdBefore(deviceId, beforeTime);
}
}

View File

@ -0,0 +1,121 @@
package com.evobms.project.battery.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evobms.project.battery.mapper.SubsystemTemperatureMapper;
import com.evobms.project.battery.domain.SubsystemTemperature;
import com.evobms.project.battery.service.ISubsystemTemperatureService;
/**
* 可充电储能子系统温度信息Service业务层处理
*
* @author 田志阳
* @date 2025-11-15
*/
@Service
public class SubsystemTemperatureServiceImpl extends ServiceImpl<SubsystemTemperatureMapper, SubsystemTemperature> implements ISubsystemTemperatureService
{
@Autowired
private SubsystemTemperatureMapper subsystemTemperatureMapper;
/**
* 查询可充电储能子系统温度信息
*
* @param id 可充电储能子系统温度信息主键
* @return 可充电储能子系统温度信息
*/
@Override
public SubsystemTemperature selectSubsystemTemperatureById(Long id)
{
return subsystemTemperatureMapper.selectSubsystemTemperatureById(id);
}
/**
* 查询可充电储能子系统温度信息列表
*
* @param subsystemTemperature 可充电储能子系统温度信息
* @return 可充电储能子系统温度信息
*/
@Override
public List<SubsystemTemperature> selectSubsystemTemperatureList(SubsystemTemperature subsystemTemperature)
{
LambdaQueryWrapper<SubsystemTemperature> qw = new LambdaQueryWrapper<>();
if (subsystemTemperature != null) {
if (subsystemTemperature.getDeviceId() != null && !subsystemTemperature.getDeviceId().isEmpty()) {
qw.eq(SubsystemTemperature::getDeviceId, subsystemTemperature.getDeviceId());
}
if (subsystemTemperature.getTimestamp() != null) {
qw.ge(SubsystemTemperature::getTimestamp, subsystemTemperature.getTimestamp());
}
java.util.Map<String, Object> params = subsystemTemperature.getParams();
if (params != null) {
java.util.Date begin = com.evobms.common.utils.DateUtils.parseDate(params.get("beginTime"));
java.util.Date end = com.evobms.common.utils.DateUtils.parseDate(params.get("endTime"));
if (begin != null) {
qw.ge(SubsystemTemperature::getTimestamp, begin);
}
if (end != null) {
qw.le(SubsystemTemperature::getTimestamp, end);
}
}
}
qw.orderByDesc(SubsystemTemperature::getTimestamp);
return subsystemTemperatureMapper.selectList(qw);
}
/**
* 新增可充电储能子系统温度信息
*
* @param subsystemTemperature 可充电储能子系统温度信息
* @return 结果
*/
@Override
public int insertSubsystemTemperature(SubsystemTemperature subsystemTemperature)
{
return subsystemTemperatureMapper.insertSubsystemTemperature(subsystemTemperature);
}
/**
* 修改可充电储能子系统温度信息
*
* @param subsystemTemperature 可充电储能子系统温度信息
* @return 结果
*/
@Override
public int updateSubsystemTemperature(SubsystemTemperature subsystemTemperature)
{
return subsystemTemperatureMapper.updateSubsystemTemperature(subsystemTemperature);
}
/**
* 批量删除可充电储能子系统温度信息
*
* @param ids 需要删除的可充电储能子系统温度信息主键
* @return 结果
*/
@Override
public int deleteSubsystemTemperatureByIds(Long[] ids)
{
return subsystemTemperatureMapper.deleteSubsystemTemperatureByIds(ids);
}
/**
* 删除可充电储能子系统温度信息信息
*
* @param id 可充电储能子系统温度信息主键
* @return 结果
*/
@Override
public int deleteSubsystemTemperatureById(Long id)
{
return subsystemTemperatureMapper.deleteSubsystemTemperatureById(id);
}
@Override
public SubsystemTemperature selectLatestByDeviceAndSubsystem(String deviceId, Integer subsystemNo) {
return subsystemTemperatureMapper.selectLatestByDeviceAndSubsystem(deviceId, subsystemNo);
}
}

View File

@ -0,0 +1,121 @@
package com.evobms.project.battery.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evobms.project.battery.mapper.SubsystemVoltageMapper;
import com.evobms.project.battery.domain.SubsystemVoltage;
import com.evobms.project.battery.service.ISubsystemVoltageService;
/**
* 可充电储能子系统电压信息Service业务层处理
*
* @author 田志阳
* @date 2025-11-15
*/
@Service
public class SubsystemVoltageServiceImpl extends ServiceImpl<SubsystemVoltageMapper, SubsystemVoltage> implements ISubsystemVoltageService
{
@Autowired
private SubsystemVoltageMapper subsystemVoltageMapper;
/**
* 查询可充电储能子系统电压信息
*
* @param id 可充电储能子系统电压信息主键
* @return 可充电储能子系统电压信息
*/
@Override
public SubsystemVoltage selectSubsystemVoltageById(Long id)
{
return subsystemVoltageMapper.selectSubsystemVoltageById(id);
}
/**
* 查询可充电储能子系统电压信息列表
*
* @param subsystemVoltage 可充电储能子系统电压信息
* @return 可充电储能子系统电压信息
*/
@Override
public List<SubsystemVoltage> selectSubsystemVoltageList(SubsystemVoltage subsystemVoltage)
{
LambdaQueryWrapper<SubsystemVoltage> qw = new LambdaQueryWrapper<>();
if (subsystemVoltage != null) {
if (subsystemVoltage.getDeviceId() != null && !subsystemVoltage.getDeviceId().isEmpty()) {
qw.eq(SubsystemVoltage::getDeviceId, subsystemVoltage.getDeviceId());
}
if (subsystemVoltage.getTimestamp() != null) {
qw.ge(SubsystemVoltage::getTimestamp, subsystemVoltage.getTimestamp());
}
java.util.Map<String, Object> params = subsystemVoltage.getParams();
if (params != null) {
java.util.Date begin = com.evobms.common.utils.DateUtils.parseDate(params.get("beginTime"));
java.util.Date end = com.evobms.common.utils.DateUtils.parseDate(params.get("endTime"));
if (begin != null) {
qw.ge(SubsystemVoltage::getTimestamp, begin);
}
if (end != null) {
qw.le(SubsystemVoltage::getTimestamp, end);
}
}
}
qw.orderByDesc(SubsystemVoltage::getTimestamp);
return subsystemVoltageMapper.selectList(qw);
}
/**
* 新增可充电储能子系统电压信息
*
* @param subsystemVoltage 可充电储能子系统电压信息
* @return 结果
*/
@Override
public int insertSubsystemVoltage(SubsystemVoltage subsystemVoltage)
{
return subsystemVoltageMapper.insertSubsystemVoltage(subsystemVoltage);
}
/**
* 修改可充电储能子系统电压信息
*
* @param subsystemVoltage 可充电储能子系统电压信息
* @return 结果
*/
@Override
public int updateSubsystemVoltage(SubsystemVoltage subsystemVoltage)
{
return subsystemVoltageMapper.updateSubsystemVoltage(subsystemVoltage);
}
/**
* 批量删除可充电储能子系统电压信息
*
* @param ids 需要删除的可充电储能子系统电压信息主键
* @return 结果
*/
@Override
public int deleteSubsystemVoltageByIds(Long[] ids)
{
return subsystemVoltageMapper.deleteSubsystemVoltageByIds(ids);
}
/**
* 删除可充电储能子系统电压信息信息
*
* @param id 可充电储能子系统电压信息主键
* @return 结果
*/
@Override
public int deleteSubsystemVoltageById(Long id)
{
return subsystemVoltageMapper.deleteSubsystemVoltageById(id);
}
@Override
public SubsystemVoltage selectLatestByDeviceAndSubsystem(String deviceId, Integer subsystemNo) {
return subsystemVoltageMapper.selectLatestByDeviceAndSubsystem(deviceId, subsystemNo);
}
}

View File

@ -0,0 +1,104 @@
package com.evobms.project.bms.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.evobms.framework.aspectj.lang.annotation.Log;
import com.evobms.framework.aspectj.lang.enums.BusinessType;
import com.evobms.project.bms.domain.BmsChargeDetail;
import com.evobms.project.bms.service.IBmsChargeDetailService;
import com.evobms.framework.web.controller.BaseController;
import com.evobms.framework.web.domain.AjaxResult;
import com.evobms.common.utils.poi.ExcelUtil;
import com.evobms.framework.web.page.TableDataInfo;
/**
* BMS充电过程详细数据Controller
*
* @author tzy
* @date 2026-02-23
*/
@RestController
@RequestMapping("/bms/chargedetail/data")
public class BmsChargeDetailController extends BaseController
{
@Autowired
private IBmsChargeDetailService bmsChargeDetailService;
/**
* 查询BMS充电过程详细数据列表
*/
@PreAuthorize("@ss.hasPermi('bms:data:list')")
@GetMapping("/list")
public TableDataInfo list(BmsChargeDetail bmsChargeDetail)
{
startPage();
List<BmsChargeDetail> list = bmsChargeDetailService.selectBmsChargeDetailList(bmsChargeDetail);
return getDataTable(list);
}
/**
* 导出BMS充电过程详细数据列表
*/
@PreAuthorize("@ss.hasPermi('bms:data:export')")
@Log(title = "BMS充电过程详细数据", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, BmsChargeDetail bmsChargeDetail)
{
List<BmsChargeDetail> list = bmsChargeDetailService.selectBmsChargeDetailList(bmsChargeDetail);
ExcelUtil<BmsChargeDetail> util = new ExcelUtil<BmsChargeDetail>(BmsChargeDetail.class);
util.exportExcel(response, list, "BMS充电过程详细数据数据");
}
/**
* 获取BMS充电过程详细数据详细信息
*/
@PreAuthorize("@ss.hasPermi('bms:data:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(bmsChargeDetailService.selectBmsChargeDetailById(id));
}
/**
* 新增BMS充电过程详细数据
*/
@PreAuthorize("@ss.hasPermi('bms:data:add')")
@Log(title = "BMS充电过程详细数据", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody BmsChargeDetail bmsChargeDetail)
{
return toAjax(bmsChargeDetailService.insertBmsChargeDetail(bmsChargeDetail));
}
/**
* 修改BMS充电过程详细数据
*/
@PreAuthorize("@ss.hasPermi('bms:data:edit')")
@Log(title = "BMS充电过程详细数据", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody BmsChargeDetail bmsChargeDetail)
{
return toAjax(bmsChargeDetailService.updateBmsChargeDetail(bmsChargeDetail));
}
/**
* 删除BMS充电过程详细数据
*/
@PreAuthorize("@ss.hasPermi('bms:data:remove')")
@Log(title = "BMS充电过程详细数据", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(bmsChargeDetailService.deleteBmsChargeDetailByIds(ids));
}
}

View File

@ -0,0 +1,104 @@
package com.evobms.project.bms.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.evobms.framework.aspectj.lang.annotation.Log;
import com.evobms.framework.aspectj.lang.enums.BusinessType;
import com.evobms.project.bms.domain.BmsChargeTemperature;
import com.evobms.project.bms.service.IBmsChargeTemperatureService;
import com.evobms.framework.web.controller.BaseController;
import com.evobms.framework.web.domain.AjaxResult;
import com.evobms.common.utils.poi.ExcelUtil;
import com.evobms.framework.web.page.TableDataInfo;
/**
* BMS充电座温度数据Controller
*
* @author tzy
* @date 2026-02-23
*/
@RestController
@RequestMapping("/bms/chargetemperature/data")
public class BmsChargeTemperatureController extends BaseController
{
@Autowired
private IBmsChargeTemperatureService bmsChargeTemperatureService;
/**
* 查询BMS充电座温度数据列表
*/
@PreAuthorize("@ss.hasPermi('bms:data:list')")
@GetMapping("/list")
public TableDataInfo list(BmsChargeTemperature bmsChargeTemperature)
{
startPage();
List<BmsChargeTemperature> list = bmsChargeTemperatureService.selectBmsChargeTemperatureList(bmsChargeTemperature);
return getDataTable(list);
}
/**
* 导出BMS充电座温度数据列表
*/
@PreAuthorize("@ss.hasPermi('bms:data:export')")
@Log(title = "BMS充电座温度数据", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, BmsChargeTemperature bmsChargeTemperature)
{
List<BmsChargeTemperature> list = bmsChargeTemperatureService.selectBmsChargeTemperatureList(bmsChargeTemperature);
ExcelUtil<BmsChargeTemperature> util = new ExcelUtil<BmsChargeTemperature>(BmsChargeTemperature.class);
util.exportExcel(response, list, "BMS充电座温度数据数据");
}
/**
* 获取BMS充电座温度数据详细信息
*/
@PreAuthorize("@ss.hasPermi('bms:data:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(bmsChargeTemperatureService.selectBmsChargeTemperatureById(id));
}
/**
* 新增BMS充电座温度数据
*/
@PreAuthorize("@ss.hasPermi('bms:data:add')")
@Log(title = "BMS充电座温度数据", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody BmsChargeTemperature bmsChargeTemperature)
{
return toAjax(bmsChargeTemperatureService.insertBmsChargeTemperature(bmsChargeTemperature));
}
/**
* 修改BMS充电座温度数据
*/
@PreAuthorize("@ss.hasPermi('bms:data:edit')")
@Log(title = "BMS充电座温度数据", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody BmsChargeTemperature bmsChargeTemperature)
{
return toAjax(bmsChargeTemperatureService.updateBmsChargeTemperature(bmsChargeTemperature));
}
/**
* 删除BMS充电座温度数据
*/
@PreAuthorize("@ss.hasPermi('bms:data:remove')")
@Log(title = "BMS充电座温度数据", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(bmsChargeTemperatureService.deleteBmsChargeTemperatureByIds(ids));
}
}

View File

@ -1,196 +0,0 @@
package com.evobms.project.bms.controller;
import com.evobms.framework.aspectj.lang.annotation.Log;
import com.evobms.framework.aspectj.lang.enums.BusinessType;
import com.evobms.framework.web.controller.BaseController;
import com.evobms.framework.web.domain.AjaxResult;
import com.evobms.framework.web.page.TableDataInfo;
import com.evobms.project.bms.domain.BmsDevice;
import com.evobms.project.bms.service.IBmsDeviceService;
//import com.evobms.project.bms.service.MqttService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* BMS设备Controller
*
* @author evobms
* @date 2025-01-22
*/
@RestController
@RequestMapping("/bms/device")
public class BmsDeviceController extends BaseController {
@Autowired
private IBmsDeviceService bmsDeviceService;
// @Autowired(required = false)
// private MqttService mqttService;
/**
* 查询BMS设备列表
*/
@PreAuthorize("@ss.hasPermi('bms:device:list')")
@GetMapping("/list")
public TableDataInfo list(BmsDevice bmsDevice) {
startPage();
List<BmsDevice> list = bmsDeviceService.selectBmsDeviceList(bmsDevice);
return getDataTable(list);
}
/**
* 获取BMS设备详细信息
*/
@PreAuthorize("@ss.hasPermi('bms:device:query')")
@GetMapping(value = "/{deviceId}")
public AjaxResult getInfo(@PathVariable("deviceId") Long deviceId) {
return success(bmsDeviceService.selectBmsDeviceByDeviceId(deviceId));
}
/**
* 新增BMS设备
*/
@PreAuthorize("@ss.hasPermi('bms:device:add')")
@Log(title = "BMS设备", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody BmsDevice bmsDevice) {
return toAjax(bmsDeviceService.insertBmsDevice(bmsDevice));
}
/**
* 修改BMS设备
*/
@PreAuthorize("@ss.hasPermi('bms:device:edit')")
@Log(title = "BMS设备", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody BmsDevice bmsDevice) {
return toAjax(bmsDeviceService.updateBmsDevice(bmsDevice));
}
/**
* 删除BMS设备
*/
@PreAuthorize("@ss.hasPermi('bms:device:remove')")
@Log(title = "BMS设备", businessType = BusinessType.DELETE)
@DeleteMapping("/{deviceIds}")
public AjaxResult remove(@PathVariable Long[] deviceIds) {
return toAjax(bmsDeviceService.deleteBmsDeviceByDeviceIds(deviceIds));
}
/**
* 批量更新设备状态
*/
@PreAuthorize("@ss.hasPermi('bms:device:edit')")
@Log(title = "BMS设备状态", businessType = BusinessType.UPDATE)
@PutMapping("/status")
public AjaxResult updateStatus(@RequestBody Map<String, Object> params) {
Long[] deviceIds = (Long[]) params.get("deviceIds");
String status = (String) params.get("status");
return toAjax(bmsDeviceService.updateBmsDeviceStatus(deviceIds, status));
}
// /**
// * 查询设备状态
// */
// @PreAuthorize("@ss.hasPermi('bms:device:query')")
// @PostMapping("/queryStatus/{deviceCode}")
// public AjaxResult queryDeviceStatus(@PathVariable String deviceCode) {
// if (mqttService != null) {
// mqttService.queryDeviceStatus(deviceCode);
// return success("设备状态查询命令已发送");
// } else {
// return error("MQTT服务未启用无法发送查询命令");
// }
// }
//
// /**
// * 重启设备
// */
// @PreAuthorize("@ss.hasPermi('bms:device:edit')")
// @Log(title = "重启BMS设备", businessType = BusinessType.UPDATE)
// @PostMapping("/restart/{deviceCode}")
// public AjaxResult restartDevice(@PathVariable String deviceCode) {
// if (mqttService != null) {
// mqttService.restartDevice(deviceCode);
// return success("设备重启命令已发送");
// } else {
// return error("MQTT服务未启用无法发送重启命令");
// }
// }
//
// /**
// * 配置设备参数
// */
// @PreAuthorize("@ss.hasPermi('bms:device:edit')")
// @Log(title = "配置BMS设备", businessType = BusinessType.UPDATE)
// @PostMapping("/config/{deviceCode}")
// public AjaxResult configDevice(@PathVariable String deviceCode, @RequestBody Map<String, Object> config) {
// if (mqttService != null) {
// mqttService.configDevice(deviceCode, config);
// return success("设备配置命令已发送");
// } else {
// return error("MQTT服务未启用无法发送配置命令");
// }
// }
//
// /**
// * 发送自定义MQTT消息
// */
// @PreAuthorize("@ss.hasPermi('bms:device:edit')")
// @Log(title = "发送MQTT消息", businessType = BusinessType.OTHER)
// @PostMapping("/mqtt/send")
// public AjaxResult sendMqttMessage(@RequestBody Map<String, String> params) {
// if (mqttService != null) {
// String topic = params.get("topic");
// String message = params.get("message");
// mqttService.sendMessage(topic, message);
// return success("MQTT消息已发送");
// } else {
// return error("MQTT服务未启用无法发送消息");
// }
// }
/**
* 获取设备统计信息
*/
@PreAuthorize("@ss.hasPermi('bms:device:list')")
@GetMapping("/statistics")
public AjaxResult getStatistics() {
Map<String, Object> statistics = new HashMap<>();
// 查询所有设备
List<BmsDevice> allDevices = bmsDeviceService.selectBmsDeviceList(new BmsDevice());
// 统计设备数量
long totalCount = allDevices.size();
long onlineCount = allDevices.stream().filter(device -> "0".equals(device.getStatus())).count();
long offlineCount = totalCount - onlineCount;
// 计算平均温度
double avgTemperature = allDevices.stream()
.filter(device -> device.getTemperature() != null)
.mapToDouble(BmsDevice::getTemperature)
.average()
.orElse(0.0);
// 计算平均剩余电量
double avgCapacity = allDevices.stream()
.filter(device -> device.getRemainingCapacity() != null)
.mapToDouble(BmsDevice::getRemainingCapacity)
.average()
.orElse(0.0);
statistics.put("totalCount", totalCount);
statistics.put("onlineCount", onlineCount);
statistics.put("offlineCount", offlineCount);
statistics.put("avgTemperature", Math.round(avgTemperature * 100.0) / 100.0);
statistics.put("avgCapacity", Math.round(avgCapacity * 100.0) / 100.0);
return success(statistics);
}
}

View File

@ -0,0 +1,104 @@
package com.evobms.project.bms.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.evobms.framework.aspectj.lang.annotation.Log;
import com.evobms.framework.aspectj.lang.enums.BusinessType;
import com.evobms.project.bms.domain.BmsRuntimeData;
import com.evobms.project.bms.service.IBmsRuntimeDataService;
import com.evobms.framework.web.controller.BaseController;
import com.evobms.framework.web.domain.AjaxResult;
import com.evobms.common.utils.poi.ExcelUtil;
import com.evobms.framework.web.page.TableDataInfo;
/**
* BMS实时运行数据Controller
*
* @author tzy
* @date 2026-02-23
*/
@RestController
@RequestMapping("/bms/runtime/data")
public class BmsRuntimeDataController extends BaseController
{
@Autowired
private IBmsRuntimeDataService bmsRuntimeDataService;
/**
* 查询BMS实时运行数据列表
*/
@PreAuthorize("@ss.hasPermi('bms:data:list')")
@GetMapping("/list")
public TableDataInfo list(BmsRuntimeData bmsRuntimeData)
{
startPage();
List<BmsRuntimeData> list = bmsRuntimeDataService.selectBmsRuntimeDataList(bmsRuntimeData);
return getDataTable(list);
}
/**
* 导出BMS实时运行数据列表
*/
@PreAuthorize("@ss.hasPermi('bms:data:export')")
@Log(title = "BMS实时运行数据", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, BmsRuntimeData bmsRuntimeData)
{
List<BmsRuntimeData> list = bmsRuntimeDataService.selectBmsRuntimeDataList(bmsRuntimeData);
ExcelUtil<BmsRuntimeData> util = new ExcelUtil<BmsRuntimeData>(BmsRuntimeData.class);
util.exportExcel(response, list, "BMS实时运行数据数据");
}
/**
* 获取BMS实时运行数据详细信息
*/
@PreAuthorize("@ss.hasPermi('bms:data:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(bmsRuntimeDataService.selectBmsRuntimeDataById(id));
}
/**
* 新增BMS实时运行数据
*/
@PreAuthorize("@ss.hasPermi('bms:data:add')")
@Log(title = "BMS实时运行数据", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody BmsRuntimeData bmsRuntimeData)
{
return toAjax(bmsRuntimeDataService.insertBmsRuntimeData(bmsRuntimeData));
}
/**
* 修改BMS实时运行数据
*/
@PreAuthorize("@ss.hasPermi('bms:data:edit')")
@Log(title = "BMS实时运行数据", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody BmsRuntimeData bmsRuntimeData)
{
return toAjax(bmsRuntimeDataService.updateBmsRuntimeData(bmsRuntimeData));
}
/**
* 删除BMS实时运行数据
*/
@PreAuthorize("@ss.hasPermi('bms:data:remove')")
@Log(title = "BMS实时运行数据", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(bmsRuntimeDataService.deleteBmsRuntimeDataByIds(ids));
}
}

View File

@ -0,0 +1,104 @@
package com.evobms.project.bms.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.evobms.framework.aspectj.lang.annotation.Log;
import com.evobms.framework.aspectj.lang.enums.BusinessType;
import com.evobms.project.bms.domain.BmsSopData;
import com.evobms.project.bms.service.IBmsSopDataService;
import com.evobms.framework.web.controller.BaseController;
import com.evobms.framework.web.domain.AjaxResult;
import com.evobms.common.utils.poi.ExcelUtil;
import com.evobms.framework.web.page.TableDataInfo;
/**
* BMS功率能力(SOP)数据Controller
*
* @author tzy
* @date 2026-02-23
*/
@RestController
@RequestMapping("/bms/sop/data")
public class BmsSopDataController extends BaseController
{
@Autowired
private IBmsSopDataService bmsSopDataService;
/**
* 查询BMS功率能力(SOP)数据列表
*/
@PreAuthorize("@ss.hasPermi('bms:data:list')")
@GetMapping("/list")
public TableDataInfo list(BmsSopData bmsSopData)
{
startPage();
List<BmsSopData> list = bmsSopDataService.selectBmsSopDataList(bmsSopData);
return getDataTable(list);
}
/**
* 导出BMS功率能力(SOP)数据列表
*/
@PreAuthorize("@ss.hasPermi('bms:data:export')")
@Log(title = "BMS功率能力(SOP)数据", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, BmsSopData bmsSopData)
{
List<BmsSopData> list = bmsSopDataService.selectBmsSopDataList(bmsSopData);
ExcelUtil<BmsSopData> util = new ExcelUtil<BmsSopData>(BmsSopData.class);
util.exportExcel(response, list, "BMS功率能力(SOP)数据数据");
}
/**
* 获取BMS功率能力(SOP)数据详细信息
*/
@PreAuthorize("@ss.hasPermi('bms:data:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(bmsSopDataService.selectBmsSopDataById(id));
}
/**
* 新增BMS功率能力(SOP)数据
*/
@PreAuthorize("@ss.hasPermi('bms:data:add')")
@Log(title = "BMS功率能力(SOP)数据", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody BmsSopData bmsSopData)
{
return toAjax(bmsSopDataService.insertBmsSopData(bmsSopData));
}
/**
* 修改BMS功率能力(SOP)数据
*/
@PreAuthorize("@ss.hasPermi('bms:data:edit')")
@Log(title = "BMS功率能力(SOP)数据", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody BmsSopData bmsSopData)
{
return toAjax(bmsSopDataService.updateBmsSopData(bmsSopData));
}
/**
* 删除BMS功率能力(SOP)数据
*/
@PreAuthorize("@ss.hasPermi('bms:data:remove')")
@Log(title = "BMS功率能力(SOP)数据", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(bmsSopDataService.deleteBmsSopDataByIds(ids));
}
}

View File

@ -0,0 +1,104 @@
package com.evobms.project.bms.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.evobms.framework.aspectj.lang.annotation.Log;
import com.evobms.framework.aspectj.lang.enums.BusinessType;
import com.evobms.project.bms.domain.BmsSystemStatus;
import com.evobms.project.bms.service.IBmsSystemStatusService;
import com.evobms.framework.web.controller.BaseController;
import com.evobms.framework.web.domain.AjaxResult;
import com.evobms.common.utils.poi.ExcelUtil;
import com.evobms.framework.web.page.TableDataInfo;
/**
* BMS系统状态数据Controller
*
* @author tzy
* @date 2026-02-23
*/
@RestController
@RequestMapping("/bms/systemstatus/data")
public class BmsSystemStatusController extends BaseController
{
@Autowired
private IBmsSystemStatusService bmsSystemStatusService;
/**
* 查询BMS系统状态数据列表
*/
@PreAuthorize("@ss.hasPermi('bms:data:list')")
@GetMapping("/list")
public TableDataInfo list(BmsSystemStatus bmsSystemStatus)
{
startPage();
List<BmsSystemStatus> list = bmsSystemStatusService.selectBmsSystemStatusList(bmsSystemStatus);
return getDataTable(list);
}
/**
* 导出BMS系统状态数据列表
*/
@PreAuthorize("@ss.hasPermi('bms:data:export')")
@Log(title = "BMS系统状态数据", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, BmsSystemStatus bmsSystemStatus)
{
List<BmsSystemStatus> list = bmsSystemStatusService.selectBmsSystemStatusList(bmsSystemStatus);
ExcelUtil<BmsSystemStatus> util = new ExcelUtil<BmsSystemStatus>(BmsSystemStatus.class);
util.exportExcel(response, list, "BMS系统状态数据数据");
}
/**
* 获取BMS系统状态数据详细信息
*/
@PreAuthorize("@ss.hasPermi('bms:data:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(bmsSystemStatusService.selectBmsSystemStatusById(id));
}
/**
* 新增BMS系统状态数据
*/
@PreAuthorize("@ss.hasPermi('bms:data:add')")
@Log(title = "BMS系统状态数据", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody BmsSystemStatus bmsSystemStatus)
{
return toAjax(bmsSystemStatusService.insertBmsSystemStatus(bmsSystemStatus));
}
/**
* 修改BMS系统状态数据
*/
@PreAuthorize("@ss.hasPermi('bms:data:edit')")
@Log(title = "BMS系统状态数据", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody BmsSystemStatus bmsSystemStatus)
{
return toAjax(bmsSystemStatusService.updateBmsSystemStatus(bmsSystemStatus));
}
/**
* 删除BMS系统状态数据
*/
@PreAuthorize("@ss.hasPermi('bms:data:remove')")
@Log(title = "BMS系统状态数据", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(bmsSystemStatusService.deleteBmsSystemStatusByIds(ids));
}
}

View File

@ -0,0 +1,104 @@
package com.evobms.project.bms.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.evobms.framework.aspectj.lang.annotation.Log;
import com.evobms.framework.aspectj.lang.enums.BusinessType;
import com.evobms.project.bms.domain.BmsVoltageChannelData;
import com.evobms.project.bms.service.IBmsVoltageChannelDataService;
import com.evobms.framework.web.controller.BaseController;
import com.evobms.framework.web.domain.AjaxResult;
import com.evobms.common.utils.poi.ExcelUtil;
import com.evobms.framework.web.page.TableDataInfo;
/**
* BMS电压通道数据Controller
*
* @author tzy
* @date 2026-02-23
*/
@RestController
@RequestMapping("/bms/voltagechannel/data")
public class BmsVoltageChannelDataController extends BaseController
{
@Autowired
private IBmsVoltageChannelDataService bmsVoltageChannelDataService;
/**
* 查询BMS电压通道数据列表
*/
@PreAuthorize("@ss.hasPermi('bms:data:list')")
@GetMapping("/list")
public TableDataInfo list(BmsVoltageChannelData bmsVoltageChannelData)
{
startPage();
List<BmsVoltageChannelData> list = bmsVoltageChannelDataService.selectBmsVoltageChannelDataList(bmsVoltageChannelData);
return getDataTable(list);
}
/**
* 导出BMS电压通道数据列表
*/
@PreAuthorize("@ss.hasPermi('bms:data:export')")
@Log(title = "BMS电压通道数据", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, BmsVoltageChannelData bmsVoltageChannelData)
{
List<BmsVoltageChannelData> list = bmsVoltageChannelDataService.selectBmsVoltageChannelDataList(bmsVoltageChannelData);
ExcelUtil<BmsVoltageChannelData> util = new ExcelUtil<BmsVoltageChannelData>(BmsVoltageChannelData.class);
util.exportExcel(response, list, "BMS电压通道数据数据");
}
/**
* 获取BMS电压通道数据详细信息
*/
@PreAuthorize("@ss.hasPermi('bms:data:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(bmsVoltageChannelDataService.selectBmsVoltageChannelDataById(id));
}
/**
* 新增BMS电压通道数据
*/
@PreAuthorize("@ss.hasPermi('bms:data:add')")
@Log(title = "BMS电压通道数据", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody BmsVoltageChannelData bmsVoltageChannelData)
{
return toAjax(bmsVoltageChannelDataService.insertBmsVoltageChannelData(bmsVoltageChannelData));
}
/**
* 修改BMS电压通道数据
*/
@PreAuthorize("@ss.hasPermi('bms:data:edit')")
@Log(title = "BMS电压通道数据", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody BmsVoltageChannelData bmsVoltageChannelData)
{
return toAjax(bmsVoltageChannelDataService.updateBmsVoltageChannelData(bmsVoltageChannelData));
}
/**
* 删除BMS电压通道数据
*/
@PreAuthorize("@ss.hasPermi('bms:data:remove')")
@Log(title = "BMS电压通道数据", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(bmsVoltageChannelDataService.deleteBmsVoltageChannelDataByIds(ids));
}
}

View File

@ -5,17 +5,30 @@ import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
public class GBT32960FullDecoder {
private static final Logger log = LoggerFactory.getLogger(GBT32960FullDecoder.class);
// 从SN码生成AES密钥
private static byte[] generateKeyFromSn(String sn) {
StringBuilder keyStr = new StringBuilder(sn);
while (keyStr.length() < 16) {
keyStr.append("0");
String s = sn == null ? "" : sn;
String key;
if ("EVO0001".equals(s)) {
StringBuilder sb = new StringBuilder(16);
sb.append("EVO0001");
while (sb.length() < 16) sb.append('0');
key = sb.toString();
} else if (s.length() >= 16) {
key = s.substring(s.length() - 16);
} else {
StringBuilder sb = new StringBuilder(16);
sb.append(s);
while (sb.length() < 16) sb.append('0');
key = sb.toString();
}
keyStr = new StringBuilder(keyStr.substring(0, 16));
return keyStr.toString().getBytes(java.nio.charset.StandardCharsets.UTF_8);
return key.getBytes(StandardCharsets.UTF_8);
}
// AES解密
@ -57,149 +70,236 @@ public class GBT32960FullDecoder {
}
// 详细的协议解析方法
public static String parseGBT32960Data(byte[] receivedData, String deviceSn) {
String bytesData = null;
try {
log.info("=== GBT32960协议数据解析 ===");
log.info("实际数据长度: " + receivedData.length + " 字节");
// 1. 检查起始位
if (receivedData.length < 2 || receivedData[0] != 0x23 || receivedData[1] != 0x23) {
log.error("无效的起始位或数据过短");
return null;
}
log.info("起始位: 0x2323 ✓");
public static String parseGBT32960Data(byte[] receivedData, String deviceSn) {
String bytesData = null;
try {
log.info("=== GBT32960协议数据解析 ===");
log.info("实际数据长度: " + receivedData.length + " 字节");
// 1. 检查起始位
if (receivedData.length < 2 || receivedData[0] != 0x23 || receivedData[1] != 0x23) {
log.error("无效的起始位或数据过短");
return null;
}
log.info("起始位: 0x2323 ✓");
// 2. 打印完整数据用于调试
log.info("完整数据: " + bytesToHex(receivedData));
// 2. 打印完整数据用于调试
log.info("完整数据: " + bytesToHex(receivedData));
// 3. 分析协议结构
int offset = 2; // 跳过起始位
// 3. 分析协议结构
int offset = 2; // 跳过起始位
// 命令标识
byte commandId = receivedData[offset++];
log.info("命令标识: 0x" + String.format("%02X", commandId));
// 命令标识
byte commandId = receivedData[offset++];
log.info("命令标识: 0x" + String.format("%02X", commandId));
// 应答标志
byte responseFlag = receivedData[offset++];
log.info("应答标志: 0x" + String.format("%02X", responseFlag));
// 应答标志
byte responseFlag = receivedData[offset++];
log.info("应答标志: 0x" + String.format("%02X", responseFlag));
// VIN码 (17字节)
byte[] vin = Arrays.copyOfRange(receivedData, offset, offset + 17);
offset += 17;
log.info("VIN码: " + bytesToHex(vin) + " (" + new String(vin).trim() + ")");
// VIN码 (17字节)
byte[] vin = Arrays.copyOfRange(receivedData, offset, offset + 17);
offset += 17;
log.info("VIN码: " + bytesToHex(vin) + " (" + new String(vin).trim() + ")");
// 加密方式
byte encryptionMethod = receivedData[offset++];
log.info("加密方式: 0x" + String.format("%02X", encryptionMethod));
// 加密方式
byte encryptionMethod = receivedData[offset++];
log.info("加密方式: 0x" + String.format("%02X", encryptionMethod));
if (encryptionMethod != 0x03) {
log.error("不支持的解密方式期望AES128(0x03)");
return null;
}
if (encryptionMethod != 0x03) {
log.error("不支持的解密方式期望AES128(0x03)");
return null;
}
// 数据单元长度 (WORD, 大端序)
int dataUnitLength = ((receivedData[offset] & 0xFF) << 8) | (receivedData[offset + 1] & 0xFF);
offset += 2;
log.info("声明的数据单元长度: " + dataUnitLength + " 字节");
// 数据单元长度 (WORD, 大端序)
int dataUnitLength = ((receivedData[offset] & 0xFF) << 8) | (receivedData[offset + 1] & 0xFF);
offset += 2;
log.info("声明的数据单元长度: " + dataUnitLength + " 字节");
// 计算当前头部长度
int headerLength = offset;
log.info("协议头部长度: " + headerLength + " 字节");
// 计算当前头部长度
int headerLength = offset;
log.info("协议头部长度: " + headerLength + " 字节");
// 剩余数据长度
int remainingDataLength = receivedData.length - headerLength;
log.info("剩余数据长度: " + remainingDataLength + " 字节");
// 剩余数据长度
int remainingDataLength = receivedData.length - headerLength;
log.info("剩余数据长度: " + remainingDataLength + " 字节");
// 4. 重新计算期望长度
// 方案1: 如果BCC是1字节
int expectedLength1 = headerLength + dataUnitLength + 1;
// 方案2: 如果BCC是2字节根据您最初描述
int expectedLength2 = headerLength + dataUnitLength + 2;
// 4. 重新计算期望长度
// 方案1: 如果BCC是1字节
int expectedLength1 = headerLength + dataUnitLength + 1;
// 方案2: 如果BCC是2字节根据您最初描述
int expectedLength2 = headerLength + dataUnitLength + 2;
log.info("期望长度 (BCC=1字节): " + expectedLength1);
log.info("期望长度 (BCC=2字节): " + expectedLength2);
log.info("实际长度: " + receivedData.length);
log.info("期望长度 (BCC=1字节): " + expectedLength1);
log.info("期望长度 (BCC=2字节): " + expectedLength2);
log.info("实际长度: " + receivedData.length);
// 5. 确定正确的数据范围
int encryptedDataStart = offset;
int encryptedDataEnd;
int bccStart;
// 5. 确定正确的数据范围
int encryptedDataStart = offset;
int encryptedDataEnd;
int bccStart;
if (receivedData.length == expectedLength1) {
// BCC为1字节
encryptedDataEnd = encryptedDataStart + dataUnitLength;
bccStart = encryptedDataEnd;
log.info("采用方案: BCC为1字节");
} else if (receivedData.length == expectedLength2) {
// BCC为2字节
encryptedDataEnd = encryptedDataStart + dataUnitLength;
bccStart = encryptedDataEnd;
log.info("采用方案: BCC为2字节");
} else {
// 自动适应使用剩余的所有数据作为加密数据
encryptedDataEnd = receivedData.length - 1; // 假设BCC为1字节
int actualDataUnitLength = encryptedDataEnd - encryptedDataStart;
log.info("长度不匹配,使用实际数据单元长度: " + actualDataUnitLength + " 字节");
dataUnitLength = actualDataUnitLength;
bccStart = encryptedDataEnd;
}
if (receivedData.length == expectedLength1) {
// BCC为1字节
encryptedDataEnd = encryptedDataStart + dataUnitLength;
bccStart = encryptedDataEnd;
log.info("采用方案: BCC为1字节");
} else if (receivedData.length == expectedLength2) {
// BCC为2字节
encryptedDataEnd = encryptedDataStart + dataUnitLength;
bccStart = encryptedDataEnd;
log.info("采用方案: BCC为2字节");
} else {
// 自动适应使用剩余的所有数据作为加密数据
encryptedDataEnd = receivedData.length - 1; // 假设BCC为1字节
int actualDataUnitLength = encryptedDataEnd - encryptedDataStart;
log.info("长度不匹配,使用实际数据单元长度: " + actualDataUnitLength + " 字节");
dataUnitLength = actualDataUnitLength;
bccStart = encryptedDataEnd;
}
// 6. 提取加密数据
byte[] encryptedData = Arrays.copyOfRange(receivedData, encryptedDataStart, encryptedDataStart + dataUnitLength);
log.info("加密数据长度: " + encryptedData.length + " 字节");
log.info("加密数据 (前64字节): " + bytesToHex(Arrays.copyOf(encryptedData, Math.min(64, encryptedData.length))) + "...");
// 6. 提取加密数据
byte[] encryptedData = Arrays.copyOfRange(receivedData, encryptedDataStart, encryptedDataStart + dataUnitLength);
log.info("加密数据长度: " + encryptedData.length + " 字节");
log.info("加密数据 (前64字节): " + bytesToHex(Arrays.copyOf(encryptedData, Math.min(64, encryptedData.length))) + "...");
if (encryptedData.length % 16 != 0) {
log.error("AES密文长度非法不是16倍数length=" + encryptedData.length);
return null;
}
// 7. 提取BCC校验码
int bccLength = receivedData.length - (encryptedDataStart + dataUnitLength);
byte[] receivedBCC = Arrays.copyOfRange(receivedData, encryptedDataStart + dataUnitLength, receivedData.length);
//log.info("BCC长度: " + bccLength + " 字节");
//log.info("接收到的BCC: " + bytesToHex(receivedBCC));
// 7. 提取BCC校验码
int bccLength = receivedData.length - (encryptedDataStart + dataUnitLength);
byte[] receivedBCC = Arrays.copyOfRange(receivedData, encryptedDataStart + dataUnitLength, receivedData.length);
//log.info("BCC长度: " + bccLength + " 字节");
//log.info("接收到的BCC: " + bytesToHex(receivedBCC));
// 8. 计算BCC校验范围从命令单元开始到加密数据结束
int bccCalcStart = 2; // 从命令标识开始 (跳过0x2323)
int bccCalcEnd = encryptedDataStart + dataUnitLength;
byte calculatedBCC = calculateBCC(receivedData, bccCalcStart, bccCalcEnd);
// 8. 计算BCC校验范围从命令单元开始到加密数据结束
int bccCalcStart = 2; // 从命令标识开始 (跳过0x2323)
int bccCalcEnd = encryptedDataStart + dataUnitLength;
byte calculatedBCC = calculateBCC(receivedData, bccCalcStart, bccCalcEnd);
log.info("BCC计算范围: 字节[" + bccCalcStart + "~" + bccCalcEnd + ")");
log.info("计算出的BCC: 0x" + String.format("%02X", calculatedBCC));
log.info("BCC计算范围: 字节[" + bccCalcStart + "~" + bccCalcEnd + ")");
log.info("计算出的BCC: 0x" + String.format("%02X", calculatedBCC));
// 验证BCC (只比较第一个字节)
if (receivedBCC.length > 0 && calculatedBCC == receivedBCC[0]) {
log.info("BCC校验通过 ✓");
} else {
log.error("BCC校验失败!");
}
// 验证BCC (只比较第一个字节)
if (receivedBCC.length > 0 && calculatedBCC == receivedBCC[0]) {
log.info("BCC校验通过 ✓");
} else {
log.error("BCC校验失败!");
}
//使用的AES密钥
byte[] aesKey = generateKeyFromSn(deviceSn);
byte[] decryptedData = decryptData(encryptedData, aesKey);
bytesData = bytesToHex(decryptedData);
log.info("解密成功!");
log.info("解密后数据长度: " + decryptedData.length + " 字节");
log.info("解密后数据 (完整): " + bytesData);
//使用的AES密钥
byte[] aesKey = generateKeyFromSn(deviceSn);
System.out.println(Arrays.toString(aesKey));
System.out.println(aesKey.length);
log.info("AES密钥: " + bytesToHex(aesKey));
byte[] decryptedData = decryptData(encryptedData, aesKey);
bytesData = bytesToHex(decryptedData);
log.info("解密成功!");
log.info("解密后数据长度: " + decryptedData.length + " 字节");
log.info("解密后数据 (完整): " + bytesData);
// 10. 解析解密后的数据单元
// parseDecryptedDataUnit(decryptedData);
// 10. 解析解密后的数据单元
// parseDecryptedDataUnit(decryptedData);
} catch (Exception e) {
log.error("解析过程中发生错误: " + e.getMessage());
e.printStackTrace();
} catch (Exception e) {
log.error("解析过程中发生错误: " + e.getMessage());
e.printStackTrace();
}
return bytesData;
}
/* public static String parseGBT32960Data(byte[] frame, String deviceSn) throws Exception {
if (frame == null || frame.length < 25) {
throw new IllegalArgumentException("报文长度非法");
}
return bytesData;
int offset = 0;
// 1. 起始符校验 0x23 0x23
if (frame[offset++] != 0x23 || frame[offset++] != 0x23) {
throw new IllegalArgumentException("非法起始位非0x2323");
}
// 2. 命令标识
byte commandId = frame[offset++];
// 3. 应答标志
byte responseFlag = frame[offset++];
// 4. VIN (17字节)
byte[] vinBytes = Arrays.copyOfRange(frame, offset, offset + 17);
String vin = new String(vinBytes).trim();
offset += 17;
// 5. 加密方式
byte encryptType = frame[offset++];
// 6. 数据单元长度 (大端)
if (offset + 2 > frame.length) {
throw new IllegalArgumentException("数据单元长度字段越界");
}
int dataUnitLength = ((frame[offset] & 0xFF) << 8) | (frame[offset + 1] & 0xFF);
offset += 2;
int headerLength = offset;
// 7. 校验整体长度
// 标准格式
// 头部 + 数据单元 + 1字节BCC
int expectedLength = headerLength + dataUnitLength + 1;
if (frame.length != expectedLength) {
throw new IllegalArgumentException("报文长度不匹配,声明长度=" + dataUnitLength + ",实际长度=" + frame.length + ",期望总长度=" + expectedLength);
}
// 8. 提取数据单元
byte[] dataUnit = Arrays.copyOfRange(frame, headerLength, headerLength + dataUnitLength
);
// 9. BCC 校验
// BCC范围从命令标识开始
byte receivedBcc = frame[frame.length - 1];
byte calculatedBcc = calculateBCC(frame, 2, headerLength + dataUnitLength
);
if (receivedBcc != calculatedBcc) {
throw new IllegalStateException(
String.format("BCC校验失败收到=0x%02X 计算=0x%02X",
receivedBcc, calculatedBcc)
);
}
// 10. 加密处理
if (encryptType == 0x01) {
// 不加密
return bytesToHex(dataUnit);
}
if (encryptType != 0x03) {
throw new UnsupportedOperationException(
"不支持的加密方式: " + encryptType);
}
// AES 必须 16倍数
if (dataUnitLength % 16 != 0) {
throw new IllegalStateException(
"AES密文长度非法不是16倍数length=" + dataUnitLength);
}
// 11. AES解密
byte[] aesKey = generateKeyFromSn(deviceSn);
byte[] decrypted = decryptData(dataUnit, aesKey);
return bytesToHex(decrypted);
}
*/
private static void parseDecryptedDataUnit(byte[] decryptedData) {
log.info("\n=== 数据单元解析 ===");
log.info("\n=== 数据单元解析 ===");
if (decryptedData == null || decryptedData.length == 0) {
log.info("解密数据为空");
log.info("解密数据为空");
return;
}
log.info("数据单元总长度: " + decryptedData.length + " 字节");
log.info("数据单元内容: " + bytesToHex(decryptedData));
log.info("数据单元总长度: " + decryptedData.length + " 字节");
log.info("数据单元内容: " + bytesToHex(decryptedData));
// 尝试解析采集时间(前6字节BCD)
if (decryptedData.length >= 6) {
@ -268,16 +368,26 @@ public class GBT32960FullDecoder {
private static String typeName(int typeId) {
switch (typeId & 0xFF) {
case 0x01: return "整车数据";
case 0x02: return "驱动电机数据";
case 0x03: return "燃料电池数据";
case 0x04: return "发动机数据";
case 0x05: return "车辆位置数据";
case 0x06: return "极值数据";
case 0x07: return "报警数据";
case 0x08: return "储能装置电压数据";
case 0x09: return "储能装置温度数据";
default: return String.format("未知类型(0x%02X)", typeId);
case 0x01:
return "整车数据";
case 0x02:
return "驱动电机数据";
case 0x03:
return "燃料电池数据";
case 0x04:
return "发动机数据";
case 0x05:
return "车辆位置数据";
case 0x06:
return "极值数据";
case 0x07:
return "报警数据";
case 0x08:
return "储能装置电压数据";
case 0x09:
return "储能装置温度数据";
default:
return String.format("未知类型(0x%02X)", typeId);
}
}

View File

@ -2,48 +2,68 @@ package com.evobms.project.bms.controller;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnProperty(name = "mqtt.legacyReceiver.enabled", havingValue = "true")
public class GBT32960MqttReceiver {
private static final String BROKER = "tcp://61.182.73.218:1883";
private static final String CLIENT_ID = "evobms-client";
private static final String TOPIC = "/bbox/EVO0002/#";
private static final String DEVICE_SN = "EVO0002";
@Value("${mqtt.host}")
private String broker;
@Value("${mqtt.clientId}")
private String clientId;
@Value("${mqtt.username:}")
private String username;
@Value("${mqtt.password:}")
private String password;
@Value("${mqtt.connectionTimeout:30}")
private int connectionTimeout;
@Value("${mqtt.keepAliveInterval:60}")
private int keepAliveInterval;
@Value("${mqtt.automaticReconnect:true}")
private boolean automaticReconnect;
@Value("${mqtt.cleanSession:true}")
private boolean cleanSession;
@Value("${mqtt.subscribeTopic:/bbox/+/#}")
private String subscribeTopic;
@Bean
public void initMQtt() throws MqttException {
MqttClient client = new MqttClient(BROKER, CLIENT_ID, new MemoryPersistence());
public void initMQtt() {
try {
MqttClient client = new MqttClient(broker, clientId, new MemoryPersistence());
MqttConnectOptions options = new MqttConnectOptions();
options.setCleanSession(true);
client.setCallback(new MqttCallback() {
@Override
public void connectionLost(Throwable cause) {
System.out.println("MQTT连接断开: " + cause.getMessage());
MqttConnectOptions options = new MqttConnectOptions();
options.setCleanSession(cleanSession);
options.setAutomaticReconnect(automaticReconnect);
options.setConnectionTimeout(connectionTimeout);
options.setKeepAliveInterval(keepAliveInterval);
if (username != null && !username.isEmpty()) {
options.setUserName(username);
}
if (password != null && !password.isEmpty()) {
options.setPassword(password.toCharArray());
}
@Override
public void messageArrived(String topic, MqttMessage message) {
System.out.println("\n收到MQTT消息主题: " + topic);
System.out.println("QoS: " + message.getQos());
client.setCallback(new MqttCallback() {
@Override
public void connectionLost(Throwable cause) {
}
// 直接处理字节数组
byte[] payload = message.getPayload();
// GBT32960FullDecoder.parseGBT32960Data(payload, DEVICE_SN);
@Override
public void messageArrived(String topic, MqttMessage message) {
byte[] payload = message.getPayload();
}
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
}
});
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
// 发布完成回调
}
});
client.connect(options);
client.subscribe(TOPIC);
System.out.println("已成功连接到MQTT Broker并订阅主题: " + TOPIC);
client.connect(options);
client.subscribe(subscribeTopic);
} catch (Exception e) {
}
}
}

View File

@ -5,6 +5,8 @@ import com.evobms.common.constant.Constants;
import com.evobms.common.constant.BboxApiConstants;
import com.evobms.common.utils.StringUtils;
import com.evobms.project.bms.service.MqttService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import javax.servlet.http.HttpServletRequest;
@ -25,6 +27,7 @@ import org.slf4j.LoggerFactory;
*/
@RestController
@RequestMapping("/bms/mqtt")
@Api(tags = "MQTT消息发布")
public class MqttPublishController {
private static final Logger log = LoggerFactory.getLogger(MqttPublishController.class);
@ -39,12 +42,12 @@ public class MqttPublishController {
*/
@PostMapping("/publish")
// @Log(title = "MQTT消息发布", businessType = BusinessType.INSERT)
@ApiOperation("发布文本消息")
public AjaxResult publishMessage(@RequestParam String topic, @RequestParam String message) {
try {
if (mqttService == null) {
return AjaxResult.error("MQTT服务未启用");
}
mqttService.sendMessage(topic, message);
log.info("发布消息成功 -> 主题: {}, 内容: {}", topic, message);
return AjaxResult.success("消息发布成功", "主题: " + topic + ", 内容: " + message);
@ -58,6 +61,7 @@ public class MqttPublishController {
* 发布原始十六进制字节消息到指定主题
*/
@PostMapping("/publishHex")
@ApiOperation("发布HEX消息")
public AjaxResult publishHex(@RequestParam String topic, @RequestParam String hex) {
try {
if (mqttService == null) {
@ -78,6 +82,7 @@ public class MqttPublishController {
* 获取当前请求的鉴权令牌从请求头读取并去除前缀
*/
@GetMapping("/authToken")
@ApiOperation("获取鉴权令牌")
public AjaxResult getAuthToken(HttpServletRequest request) {
try {
String raw = request.getHeader(tokenHeader);
@ -129,6 +134,7 @@ public class MqttPublishController {
*/
@PostMapping("/publishDeviceData")
// @Log(title = "设备数据发布", businessType = BusinessType.INSERT)
@ApiOperation("发布设备测试数据")
public AjaxResult publishDeviceData(@RequestParam String deviceCode) {
try {
if (mqttService == null) {
@ -160,6 +166,7 @@ public class MqttPublishController {
* 快速测试 - 发布消息到test设备
*/
@GetMapping("/quickTest")
@ApiOperation("快速测试发布")
public AjaxResult quickTest() {
try {
if (mqttService == null) {
@ -192,6 +199,7 @@ public class MqttPublishController {
*/
@PostMapping("/batchPublish")
// @Log(title = "批量MQTT消息发布", businessType = BusinessType.INSERT)
@ApiOperation("批量发布测试消息")
public AjaxResult batchPublish(@RequestParam String deviceCode, @RequestParam(defaultValue = "5") int count) {
try {
if (mqttService == null) {

View File

@ -1,6 +1,8 @@
package com.evobms.project.bms.controller;
import com.evobms.project.bms.service.MqttService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.slf4j.Logger;
@ -17,6 +19,7 @@ import java.util.Map;
*/
@RestController
@RequestMapping("/mqtt/test")
@Api(tags = "MQTT测试")
public class MqttTestController {
private static final Logger log = LoggerFactory.getLogger(MqttTestController.class);
@ -31,12 +34,11 @@ public class MqttTestController {
* @return 测试结果
*/
@PostMapping("/handleDeviceData")
@ApiOperation("测试处理设备数据")
public Map<String, Object> testHandleDeviceData(
@RequestParam String topic,
@RequestParam byte[] payload) {
Map<String, Object> result = new HashMap<>();
try {
// 调用handleDeviceData方法
mqttService.handleDeviceData(topic, payload);
@ -46,14 +48,12 @@ public class MqttTestController {
result.put("message", "消息处理成功");
result.put("topic", topic);
result.put("payload", payload);
// 解析主题获取设备编号模拟方法内部逻辑
String[] topicParts = topic.split("/");
if (topicParts.length >= 3) {
String deviceCode = topicParts[1];
result.put("extractedDeviceCode", deviceCode);
}
} catch (Exception e) {
log.error("测试处理设备数据发生错误: {}", e.getMessage(), e);
result.put("success", false);
@ -66,10 +66,10 @@ public class MqttTestController {
/**
* 生成测试数据
*
* @return 测试用的JSON数据
*/
@GetMapping("/generateTestData")
@ApiOperation("生成测试数据")
public Map<String, Object> generateTestData() {
Map<String, Object> testData = new HashMap<>();
testData.put("voltage", 12.5);

View File

@ -0,0 +1,101 @@
package com.evobms.project.bms.domain;
import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.github.xiaoymin.knife4j.annotations.Ignore;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.evobms.framework.aspectj.lang.annotation.Excel;
import com.evobms.framework.web.domain.BaseEntity;
/**
* BMS充电过程详细数据对象 bms_charge_detail
*
* @author tzy
* @date 2026-02-23
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class BmsChargeDetail extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键ID */
private Long id;
/** 设备ID */
@Excel(name = "设备ID")
private String deviceId;
/** 数据时间戳 */
@JsonFormat(pattern = "yyyy-MM-dd")
@ApiModelProperty(value = "数据时间戳")
@Excel(name = "数据时间戳", width = 30, dateFormat = "yyyy-MM-dd")
private Date timestamp;
/** 慢充电流(A) */
@Excel(name = "慢充电流(A)")
@ApiModelProperty(value = "慢充电流(A)")
private BigDecimal acChargeCurrent;
/** 慢充电压(V) */
@Excel(name = "慢充电压(V)")
@ApiModelProperty(value = "慢充电压(V)")
private BigDecimal acChargeVoltage;
/** 快充电流(A) */
@Excel(name = "快充电流(A)")
@ApiModelProperty(value = "快充电流(A)")
private BigDecimal dcChargeCurrent;
/** 快充电压(V) */
@Excel(name = "快充电压(V)")
@ApiModelProperty(value = "快充电压(V)")
private BigDecimal dcChargeVoltage;
/** 充电时长(min) */
@Excel(name = "充电时长(min)")
@ApiModelProperty(value = "充电时长min")
private Long chargeTime;
/** 慢充停止码 */
@Excel(name = "慢充停止码")
@ApiModelProperty(value = "慢充停止码")
private Integer acStopCode;
/** 快充停止码 */
@Excel(name = "快充停止码")
@ApiModelProperty(value = "快充停止码")
private Integer dcStopCode;
/** 慢充CP值 */
@Excel(name = "慢充CP值")
@ApiModelProperty(value = "慢充CP值")
private Integer acCpValue;
/** 慢充枪阻值 */
@Excel(name = "慢充枪阻值")
@ApiModelProperty(value = "慢充枪阻值")
private Integer acGunResistance;
/** 充电阶段 */
@Excel(name = "充电阶段")
@ApiModelProperty(value = "充电阶段")
private Integer chargingStage;
/** 慢充阶段 */
@Excel(name = "慢充阶段")
@ApiModelProperty(value = "慢充阶段")
private Integer acStage;
/** 快充阶段 */
@Excel(name = "快充阶段")
@ApiModelProperty(value = "快充阶段")
private Integer dcStage;
}

View File

@ -0,0 +1,51 @@
package com.evobms.project.bms.domain;
import java.io.Serial;
import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.evobms.framework.aspectj.lang.annotation.Excel;
import com.evobms.framework.web.domain.BaseEntity;
/**
* BMS充电座温度数据对象 bms_charge_temperature
*
* @author tzy
* @date 2026-02-23
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class BmsChargeTemperature extends BaseEntity
{
@Serial
private static final long serialVersionUID = 1L;
/** 主键ID */
private Long id;
/** 设备ID */
@Excel(name = "设备ID")
private String deviceId;
/** 数据时间戳 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "数据时间戳", width = 30, dateFormat = "yyyy-MM-dd")
@ApiModelProperty(value = "数据时间戳")
private Date timestamp;
/** 温度编号(1-8) */
@Excel(name = "温度编号(1-8)")
@ApiModelProperty(value = "温度编号(1-8)")
private Integer tempIndex;
/** 温度(℃) */
@Excel(name = "温度(℃)")
@ApiModelProperty(value = "温度(℃)")
private BigDecimal temperature;
}

View File

@ -1,203 +0,0 @@
package com.evobms.project.bms.domain;
import com.baomidou.mybatisplus.annotation.*;
import com.evobms.framework.aspectj.lang.annotation.Excel;
import com.evobms.framework.web.domain.BaseEntity;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;
/**
* BMS设备对象 bms_device
*
* @author evobms
* @date 2025-01-22
*/
@TableName("bms_device")
public class BmsDevice extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 设备ID */
@TableId(type = IdType.AUTO)
private Long deviceId;
/** 设备编号 */
@Excel(name = "设备编号")
private String deviceCode;
/** 设备名称 */
@Excel(name = "设备名称")
private String deviceName;
/** 设备类型 */
@Excel(name = "设备类型")
private String deviceType;
/** 设备状态0正常 1停用 */
@Excel(name = "设备状态", readConverterExp = "0=正常,1=停用")
private String status;
/** 电池容量(Ah) */
@Excel(name = "电池容量")
private Double batteryCapacity;
/** 额定电压(V) */
@Excel(name = "额定电压")
private Double ratedVoltage;
/** 当前电压(V) */
private Double currentVoltage;
/** 当前电流(A) */
private Double currentCurrent;
/** 剩余电量(%) */
private Double remainingCapacity;
/** 温度(℃) */
private Double temperature;
/** 最后通信时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date lastCommTime;
/** 删除标志0代表存在 2代表删除 */
@TableLogic
private String delFlag;
/** 版本号(乐观锁) */
@Version
private Integer version;
public void setDeviceId(Long deviceId) {
this.deviceId = deviceId;
}
public Long getDeviceId() {
return deviceId;
}
public void setDeviceCode(String deviceCode) {
this.deviceCode = deviceCode;
}
public String getDeviceCode() {
return deviceCode;
}
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public String getDeviceName() {
return deviceName;
}
public void setDeviceType(String deviceType) {
this.deviceType = deviceType;
}
public String getDeviceType() {
return deviceType;
}
public void setStatus(String status) {
this.status = status;
}
public String getStatus() {
return status;
}
public void setBatteryCapacity(Double batteryCapacity) {
this.batteryCapacity = batteryCapacity;
}
public Double getBatteryCapacity() {
return batteryCapacity;
}
public void setRatedVoltage(Double ratedVoltage) {
this.ratedVoltage = ratedVoltage;
}
public Double getRatedVoltage() {
return ratedVoltage;
}
public void setCurrentVoltage(Double currentVoltage) {
this.currentVoltage = currentVoltage;
}
public Double getCurrentVoltage() {
return currentVoltage;
}
public void setCurrentCurrent(Double currentCurrent) {
this.currentCurrent = currentCurrent;
}
public Double getCurrentCurrent() {
return currentCurrent;
}
public void setRemainingCapacity(Double remainingCapacity) {
this.remainingCapacity = remainingCapacity;
}
public Double getRemainingCapacity() {
return remainingCapacity;
}
public void setTemperature(Double temperature) {
this.temperature = temperature;
}
public Double getTemperature() {
return temperature;
}
public void setLastCommTime(Date lastCommTime) {
this.lastCommTime = lastCommTime;
}
public Date getLastCommTime() {
return lastCommTime;
}
public void setDelFlag(String delFlag) {
this.delFlag = delFlag;
}
public String getDelFlag() {
return delFlag;
}
public void setVersion(Integer version) {
this.version = version;
}
public Integer getVersion() {
return version;
}
@Override
public String toString() {
return "BmsDevice{" +
"deviceId=" + deviceId +
", deviceCode='" + deviceCode + '\'' +
", deviceName='" + deviceName + '\'' +
", deviceType='" + deviceType + '\'' +
", status='" + status + '\'' +
", batteryCapacity=" + batteryCapacity +
", ratedVoltage=" + ratedVoltage +
", currentVoltage=" + currentVoltage +
", currentCurrent=" + currentCurrent +
", remainingCapacity=" + remainingCapacity +
", temperature=" + temperature +
", lastCommTime=" + lastCommTime +
", delFlag='" + delFlag + '\'' +
", version=" + version +
'}';
}
}

View File

@ -0,0 +1,91 @@
package com.evobms.project.bms.domain;
import java.io.Serial;
import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.evobms.framework.aspectj.lang.annotation.Excel;
import com.evobms.framework.web.domain.BaseEntity;
/**
* BMS实时运行数据对象 bms_runtime_data
*
* @author tzy
* @date 2026-02-23
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class BmsRuntimeData extends BaseEntity
{
@Serial
private static final long serialVersionUID = 1L;
/** 主键ID */
private Long id;
/** 设备ID */
@Excel(name = "设备ID")
private String deviceId;
/** 数据时间戳 */
@JsonFormat(pattern = "yyyy-MM-dd")
@ApiModelProperty(value = "数据时间戳")
@Excel(name = "数据时间戳", width = 30, dateFormat = "yyyy-MM-dd")
private Date timestamp;
/** 车速(km/h) */
@Excel(name = "车速(km/h)")
@ApiModelProperty(value = "车速(km/h)")
private BigDecimal vehicleSpeed;
/** 总里程(km) */
@Excel(name = "总里程(km)")
@ApiModelProperty(value = "总里程(km)")
private Long mileage;
/** 系统电压(V) */
@Excel(name = "系统电压(V)")
@ApiModelProperty(value = "系统电压(V)")
private BigDecimal systemVoltage;
/** 系统电流(A) */
@Excel(name = "系统电流(A)")
@ApiModelProperty(value = "系统电流(A)")
private BigDecimal systemCurrent;
/** 绝缘电阻(KΩ) */
@Excel(name = "绝缘电阻(KΩ)")
@ApiModelProperty(value = "绝缘电阻(KΩ)")
private Long insulationResistance;
/** 最大SOC(%) */
@Excel(name = "最大SOC(%)")
@ApiModelProperty(value = "最大SOC(%)")
private BigDecimal socMax;
/** 最小SOC(%) */
@Excel(name = "最小SOC(%)")
@ApiModelProperty(value = "最小SOC(%)")
private BigDecimal socMin;
/** GPV电压(V) */
@Excel(name = "GPV电压(V)")
@ApiModelProperty(value = "GPV电压(V)")
private BigDecimal gpvVoltage;
/** 行车阶段 */
@Excel(name = "行车阶段")
@ApiModelProperty(value = "行车阶段")
private Integer runStatus;
/** 行车上电阶段 */
@Excel(name = "行车上电阶段")
@ApiModelProperty(value = "行车上电阶段")
private Integer powerStatus;
}

View File

@ -0,0 +1,113 @@
package com.evobms.project.bms.domain;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.evobms.framework.aspectj.lang.annotation.Excel;
import com.evobms.framework.web.domain.BaseEntity;
/**
* BMS功率能力(SOP)数据对象 bms_sop_data
*
* @author tzy
* @date 2026-02-23
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class BmsSopData extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键ID */
private Long id;
/** 设备ID */
@Excel(name = "设备ID")
private String deviceId;
/** 数据时间戳 */
@JsonFormat(pattern = "yyyy-MM-dd")
@ApiModelProperty(value = "数据时间戳")
@Excel(name = "数据时间戳", width = 30, dateFormat = "yyyy-MM-dd")
@TableField("`timestamp`")
private Date timestamp;
/** 10秒放电SOP(A) */
@Excel(name = "10秒放电SOP(A)")
@ApiModelProperty(value = "10秒放电SOP(A)")
@TableField("discharge_sop_10s")
private Long dischargeSop10s;
/** 30秒放电SOP(A) */
@Excel(name = "30秒放电SOP(A)")
@ApiModelProperty(value = "30秒放电SOP(A)")
@TableField("discharge_sop_30s")
private Long dischargeSop30s;
/** 60秒放电SOP(A) */
@Excel(name = "60秒放电SOP(A)")
@ApiModelProperty(value = "60秒放电SOP(A)")
@TableField("discharge_sop_60s")
private Long dischargeSop60s;
/** 持续放电SOP(A) */
@Excel(name = "持续放电SOP(A)")
@ApiModelProperty(value = "持续放电SOP(A)")
@TableField("discharge_sop_continuous")
private Long dischargeSopContinuous;
/** 放电融合SOP(A) */
@Excel(name = "放电融合SOP(A)")
@ApiModelProperty(value = "放电融合SOP(A)")
private Long dischargeSopTotal;
/** 10秒回馈SOP(A) */
@Excel(name = "10秒回馈SOP(A)")
@ApiModelProperty(value = "10秒回馈SOP(A)")
@TableField("regen_sop_10s")
private Long regenSop10s;
/** 30秒回馈SOP(A) */
@Excel(name = "30秒回馈SOP(A)")
@ApiModelProperty(value = "30秒回馈SOP(A)")
@TableField("regen_sop_30s")
private Long regenSop30s;
/** 60秒回馈SOP(A) */
@Excel(name = "60秒回馈SOP(A)")
@ApiModelProperty(value = "60秒回馈SOP(A)")
@TableField("regen_sop_60s")
private Long regenSop60s;
/** 持续回馈SOP(A) */
@Excel(name = "持续回馈SOP(A)")
@ApiModelProperty(value = "持续回馈SOP(A)")
@TableField("regen_sop_continuous")
private Long regenSopContinuous;
/** 回馈融合SOP(A) */
@Excel(name = "回馈融合SOP(A)")
@ApiModelProperty(value = "回馈融合SOP(A)")
private Long regenSopTotal;
/** 充电SOP(A) */
@Excel(name = "充电SOP(A)")
@ApiModelProperty(value = "充电SOP(A)")
@TableField("charge_sop")
private Long chargeSop;
/** 慢充SOP(A) */
@Excel(name = "慢充SOP(A)")
@ApiModelProperty(value = "慢充SOP(A)")
@TableField("ac_charge_sop")
private Long acChargeSop;
}

View File

@ -0,0 +1,82 @@
package com.evobms.project.bms.domain;
import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.evobms.framework.aspectj.lang.annotation.Excel;
import com.evobms.framework.web.domain.BaseEntity;
/**
* BMS系统状态数据对象 bms_system_status
*
* @author tzy
* @date 2026-02-23
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class BmsSystemStatus extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键ID */
private Long id;
/** 设备ID */
@Excel(name = "设备ID")
private String deviceId;
/** 数据时间戳 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "数据时间戳", width = 30, dateFormat = "yyyy-MM-dd")
@ApiModelProperty(value = "数据时间戳")
private Date timestamp;
/** 继电器状态 */
@Excel(name = "继电器状态")
@ApiModelProperty(value = "继电器状态")
private Long relayStatus;
/** ON/A+/CC状态 */
@Excel(name = "ON/A+/CC状态")
@ApiModelProperty(value = "ON/A+/CC状态")
private Long onaaccStatus;
/** 系统复位次数 */
@Excel(name = "系统复位次数")
@ApiModelProperty(value = "系统复位次数")
private Long resetCount;
/** 电池循环次数 */
@Excel(name = "电池循环次数")
@ApiModelProperty(value = "电池循环次数")
private Long cycleCount;
/** 电池循环次数 */
@Excel(name = "电池芯单体数")
@ApiModelProperty(value = "电池芯单体数")
private Long cellCount;
/** 2.5V采样电压(V) */
@Excel(name = "2.5V采样电压(V)")
@ApiModelProperty(value = "2.5V采样电压(V)")
@TableField("sample_25v")
private BigDecimal sample25v;
/** 5V采样电压(V) */
@Excel(name = "5V采样电压(V)")
@ApiModelProperty(value = "5V采样电压(V)")
@TableField("sample_5v")
private BigDecimal sample5v;
/** 测温点数量(V) */
@Excel(name = "测温点数量(V)")
@ApiModelProperty(value = "测温点数量(V)")
@TableField("temp_sensor_count")
private Integer tempSensorCount;
}

View File

@ -0,0 +1,54 @@
package com.evobms.project.bms.domain;
import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.evobms.framework.aspectj.lang.annotation.Excel;
import com.evobms.framework.web.domain.BaseEntity;
/**
* BMS电压通道数据对象 bms_voltage_channel_data
*
* @author tzy
* @date 2026-02-23
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class BmsVoltageChannelData extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键ID */
private Long id;
/** 设备ID */
@Excel(name = "设备ID")
private String deviceId;
/** 数据时间戳 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "数据时间戳", width = 30, dateFormat = "yyyy-MM-dd")
@ApiModelProperty(value = "数据时间戳")
private Date timestamp;
/** 通道类型(RTP/RTN) */
@Excel(name = "通道类型(RTP/RTN)")
@ApiModelProperty(value = "通道类型(RTP/RTN)")
private String channelType;
/** 通道编号 */
@Excel(name = "通道编号")
@ApiModelProperty(value = "通道编号")
private Integer channelIndex;
/** 电压(V) */
@Excel(name = "电压(V)")
@ApiModelProperty(value = "电压(V)")
private BigDecimal voltage;
}

View File

@ -0,0 +1,61 @@
package com.evobms.project.bms.mapper;
import java.util.List;
import com.evobms.project.bms.domain.BmsChargeDetail;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* BMS充电过程详细数据Mapper接口
*
* @author tzy
* @date 2026-02-23
*/
public interface BmsChargeDetailMapper extends BaseMapper<BmsChargeDetail> {
/**
* 查询BMS充电过程详细数据
*
* @param id BMS充电过程详细数据主键
* @return BMS充电过程详细数据
*/
public BmsChargeDetail selectBmsChargeDetailById(Long id);
/**
* 查询BMS充电过程详细数据列表
*
* @param bmsChargeDetail BMS充电过程详细数据
* @return BMS充电过程详细数据集合
*/
public List<BmsChargeDetail> selectBmsChargeDetailList(BmsChargeDetail bmsChargeDetail);
/**
* 新增BMS充电过程详细数据
*
* @param bmsChargeDetail BMS充电过程详细数据
* @return 结果
*/
public int insertBmsChargeDetail(BmsChargeDetail bmsChargeDetail);
/**
* 修改BMS充电过程详细数据
*
* @param bmsChargeDetail BMS充电过程详细数据
* @return 结果
*/
public int updateBmsChargeDetail(BmsChargeDetail bmsChargeDetail);
/**
* 删除BMS充电过程详细数据
*
* @param id BMS充电过程详细数据主键
* @return 结果
*/
public int deleteBmsChargeDetailById(Long id);
/**
* 批量删除BMS充电过程详细数据
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteBmsChargeDetailByIds(Long[] ids);
}

View File

@ -0,0 +1,61 @@
package com.evobms.project.bms.mapper;
import java.util.List;
import com.evobms.project.bms.domain.BmsChargeTemperature;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* BMS充电座温度数据Mapper接口
*
* @author tzy
* @date 2026-02-23
*/
public interface BmsChargeTemperatureMapper extends BaseMapper<BmsChargeTemperature> {
/**
* 查询BMS充电座温度数据
*
* @param id BMS充电座温度数据主键
* @return BMS充电座温度数据
*/
public BmsChargeTemperature selectBmsChargeTemperatureById(Long id);
/**
* 查询BMS充电座温度数据列表
*
* @param bmsChargeTemperature BMS充电座温度数据
* @return BMS充电座温度数据集合
*/
public List<BmsChargeTemperature> selectBmsChargeTemperatureList(BmsChargeTemperature bmsChargeTemperature);
/**
* 新增BMS充电座温度数据
*
* @param bmsChargeTemperature BMS充电座温度数据
* @return 结果
*/
public int insertBmsChargeTemperature(BmsChargeTemperature bmsChargeTemperature);
/**
* 修改BMS充电座温度数据
*
* @param bmsChargeTemperature BMS充电座温度数据
* @return 结果
*/
public int updateBmsChargeTemperature(BmsChargeTemperature bmsChargeTemperature);
/**
* 删除BMS充电座温度数据
*
* @param id BMS充电座温度数据主键
* @return 结果
*/
public int deleteBmsChargeTemperatureById(Long id);
/**
* 批量删除BMS充电座温度数据
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteBmsChargeTemperatureByIds(Long[] ids);
}

View File

@ -1,43 +0,0 @@
package com.evobms.project.bms.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.evobms.project.bms.domain.BmsDevice;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* BMS设备Mapper接口
*
* @author evobms
* @date 2025-01-22
*/
@Mapper
public interface BmsDeviceMapper extends BaseMapper<BmsDevice> {
/**
* 查询BMS设备列表
*
* @param bmsDevice BMS设备
* @return BMS设备集合
*/
List<BmsDevice> selectBmsDeviceList(BmsDevice bmsDevice);
/**
* 根据设备编号查询设备信息
*
* @param deviceCode 设备编号
* @return BMS设备
*/
BmsDevice selectBmsDeviceByCode(@Param("deviceCode") String deviceCode);
/**
* 批量更新设备状态
*
* @param deviceIds 设备ID数组
* @param status 状态
* @return 结果
*/
int updateBmsDeviceStatus(@Param("deviceIds") Long[] deviceIds, @Param("status") String status);
}

View File

@ -0,0 +1,61 @@
package com.evobms.project.bms.mapper;
import java.util.List;
import com.evobms.project.bms.domain.BmsRuntimeData;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* BMS实时运行数据Mapper接口
*
* @author tzy
* @date 2026-02-23
*/
public interface BmsRuntimeDataMapper extends BaseMapper<BmsRuntimeData> {
/**
* 查询BMS实时运行数据
*
* @param id BMS实时运行数据主键
* @return BMS实时运行数据
*/
public BmsRuntimeData selectBmsRuntimeDataById(Long id);
/**
* 查询BMS实时运行数据列表
*
* @param bmsRuntimeData BMS实时运行数据
* @return BMS实时运行数据集合
*/
public List<BmsRuntimeData> selectBmsRuntimeDataList(BmsRuntimeData bmsRuntimeData);
/**
* 新增BMS实时运行数据
*
* @param bmsRuntimeData BMS实时运行数据
* @return 结果
*/
public int insertBmsRuntimeData(BmsRuntimeData bmsRuntimeData);
/**
* 修改BMS实时运行数据
*
* @param bmsRuntimeData BMS实时运行数据
* @return 结果
*/
public int updateBmsRuntimeData(BmsRuntimeData bmsRuntimeData);
/**
* 删除BMS实时运行数据
*
* @param id BMS实时运行数据主键
* @return 结果
*/
public int deleteBmsRuntimeDataById(Long id);
/**
* 批量删除BMS实时运行数据
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteBmsRuntimeDataByIds(Long[] ids);
}

View File

@ -0,0 +1,61 @@
package com.evobms.project.bms.mapper;
import java.util.List;
import com.evobms.project.bms.domain.BmsSopData;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* BMS功率能力(SOP)数据Mapper接口
*
* @author tzy
* @date 2026-02-23
*/
public interface BmsSopDataMapper extends BaseMapper<BmsSopData> {
/**
* 查询BMS功率能力(SOP)数据
*
* @param id BMS功率能力(SOP)数据主键
* @return BMS功率能力(SOP)数据
*/
public BmsSopData selectBmsSopDataById(Long id);
/**
* 查询BMS功率能力(SOP)数据列表
*
* @param bmsSopData BMS功率能力(SOP)数据
* @return BMS功率能力(SOP)数据集合
*/
public List<BmsSopData> selectBmsSopDataList(BmsSopData bmsSopData);
/**
* 新增BMS功率能力(SOP)数据
*
* @param bmsSopData BMS功率能力(SOP)数据
* @return 结果
*/
public int insertBmsSopData(BmsSopData bmsSopData);
/**
* 修改BMS功率能力(SOP)数据
*
* @param bmsSopData BMS功率能力(SOP)数据
* @return 结果
*/
public int updateBmsSopData(BmsSopData bmsSopData);
/**
* 删除BMS功率能力(SOP)数据
*
* @param id BMS功率能力(SOP)数据主键
* @return 结果
*/
public int deleteBmsSopDataById(Long id);
/**
* 批量删除BMS功率能力(SOP)数据
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteBmsSopDataByIds(Long[] ids);
}

View File

@ -0,0 +1,61 @@
package com.evobms.project.bms.mapper;
import java.util.List;
import com.evobms.project.bms.domain.BmsSystemStatus;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* BMS系统状态数据Mapper接口
*
* @author tzy
* @date 2026-02-23
*/
public interface BmsSystemStatusMapper extends BaseMapper<BmsSystemStatus> {
/**
* 查询BMS系统状态数据
*
* @param id BMS系统状态数据主键
* @return BMS系统状态数据
*/
public BmsSystemStatus selectBmsSystemStatusById(Long id);
/**
* 查询BMS系统状态数据列表
*
* @param bmsSystemStatus BMS系统状态数据
* @return BMS系统状态数据集合
*/
public List<BmsSystemStatus> selectBmsSystemStatusList(BmsSystemStatus bmsSystemStatus);
/**
* 新增BMS系统状态数据
*
* @param bmsSystemStatus BMS系统状态数据
* @return 结果
*/
public int insertBmsSystemStatus(BmsSystemStatus bmsSystemStatus);
/**
* 修改BMS系统状态数据
*
* @param bmsSystemStatus BMS系统状态数据
* @return 结果
*/
public int updateBmsSystemStatus(BmsSystemStatus bmsSystemStatus);
/**
* 删除BMS系统状态数据
*
* @param id BMS系统状态数据主键
* @return 结果
*/
public int deleteBmsSystemStatusById(Long id);
/**
* 批量删除BMS系统状态数据
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteBmsSystemStatusByIds(Long[] ids);
}

View File

@ -0,0 +1,61 @@
package com.evobms.project.bms.mapper;
import java.util.List;
import com.evobms.project.bms.domain.BmsVoltageChannelData;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* BMS电压通道数据Mapper接口
*
* @author tzy
* @date 2026-02-23
*/
public interface BmsVoltageChannelDataMapper extends BaseMapper<BmsVoltageChannelData> {
/**
* 查询BMS电压通道数据
*
* @param id BMS电压通道数据主键
* @return BMS电压通道数据
*/
public BmsVoltageChannelData selectBmsVoltageChannelDataById(Long id);
/**
* 查询BMS电压通道数据列表
*
* @param bmsVoltageChannelData BMS电压通道数据
* @return BMS电压通道数据集合
*/
public List<BmsVoltageChannelData> selectBmsVoltageChannelDataList(BmsVoltageChannelData bmsVoltageChannelData);
/**
* 新增BMS电压通道数据
*
* @param bmsVoltageChannelData BMS电压通道数据
* @return 结果
*/
public int insertBmsVoltageChannelData(BmsVoltageChannelData bmsVoltageChannelData);
/**
* 修改BMS电压通道数据
*
* @param bmsVoltageChannelData BMS电压通道数据
* @return 结果
*/
public int updateBmsVoltageChannelData(BmsVoltageChannelData bmsVoltageChannelData);
/**
* 删除BMS电压通道数据
*
* @param id BMS电压通道数据主键
* @return 结果
*/
public int deleteBmsVoltageChannelDataById(Long id);
/**
* 批量删除BMS电压通道数据
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteBmsVoltageChannelDataByIds(Long[] ids);
}

View File

@ -0,0 +1,61 @@
package com.evobms.project.bms.service;
import java.util.List;
import com.evobms.project.bms.domain.BmsChargeDetail;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* BMS充电过程详细数据Service接口
*
* @author tzy
* @date 2026-02-23
*/
public interface IBmsChargeDetailService extends IService<BmsChargeDetail> {
/**
* 查询BMS充电过程详细数据
*
* @param id BMS充电过程详细数据主键
* @return BMS充电过程详细数据
*/
public BmsChargeDetail selectBmsChargeDetailById(Long id);
/**
* 查询BMS充电过程详细数据列表
*
* @param bmsChargeDetail BMS充电过程详细数据
* @return BMS充电过程详细数据集合
*/
public List<BmsChargeDetail> selectBmsChargeDetailList(BmsChargeDetail bmsChargeDetail);
/**
* 新增BMS充电过程详细数据
*
* @param bmsChargeDetail BMS充电过程详细数据
* @return 结果
*/
public int insertBmsChargeDetail(BmsChargeDetail bmsChargeDetail);
/**
* 修改BMS充电过程详细数据
*
* @param bmsChargeDetail BMS充电过程详细数据
* @return 结果
*/
public int updateBmsChargeDetail(BmsChargeDetail bmsChargeDetail);
/**
* 批量删除BMS充电过程详细数据
*
* @param ids 需要删除的BMS充电过程详细数据主键集合
* @return 结果
*/
public int deleteBmsChargeDetailByIds(Long[] ids);
/**
* 删除BMS充电过程详细数据信息
*
* @param id BMS充电过程详细数据主键
* @return 结果
*/
public int deleteBmsChargeDetailById(Long id);
}

View File

@ -0,0 +1,61 @@
package com.evobms.project.bms.service;
import java.util.List;
import com.evobms.project.bms.domain.BmsChargeTemperature;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* BMS充电座温度数据Service接口
*
* @author tzy
* @date 2026-02-23
*/
public interface IBmsChargeTemperatureService extends IService<BmsChargeTemperature> {
/**
* 查询BMS充电座温度数据
*
* @param id BMS充电座温度数据主键
* @return BMS充电座温度数据
*/
public BmsChargeTemperature selectBmsChargeTemperatureById(Long id);
/**
* 查询BMS充电座温度数据列表
*
* @param bmsChargeTemperature BMS充电座温度数据
* @return BMS充电座温度数据集合
*/
public List<BmsChargeTemperature> selectBmsChargeTemperatureList(BmsChargeTemperature bmsChargeTemperature);
/**
* 新增BMS充电座温度数据
*
* @param bmsChargeTemperature BMS充电座温度数据
* @return 结果
*/
public int insertBmsChargeTemperature(BmsChargeTemperature bmsChargeTemperature);
/**
* 修改BMS充电座温度数据
*
* @param bmsChargeTemperature BMS充电座温度数据
* @return 结果
*/
public int updateBmsChargeTemperature(BmsChargeTemperature bmsChargeTemperature);
/**
* 批量删除BMS充电座温度数据
*
* @param ids 需要删除的BMS充电座温度数据主键集合
* @return 结果
*/
public int deleteBmsChargeTemperatureByIds(Long[] ids);
/**
* 删除BMS充电座温度数据信息
*
* @param id BMS充电座温度数据主键
* @return 结果
*/
public int deleteBmsChargeTemperatureById(Long id);
}

View File

@ -1,80 +0,0 @@
package com.evobms.project.bms.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.evobms.project.bms.domain.BmsDevice;
import java.util.List;
/**
* BMS设备Service接口
*
* @author evobms
* @date 2025-01-22
*/
public interface IBmsDeviceService extends IService<BmsDevice> {
/**
* 查询BMS设备
*
* @param deviceId BMS设备主键
* @return BMS设备
*/
BmsDevice selectBmsDeviceByDeviceId(Long deviceId);
/**
* 查询BMS设备列表
*
* @param bmsDevice BMS设备
* @return BMS设备集合
*/
List<BmsDevice> selectBmsDeviceList(BmsDevice bmsDevice);
/**
* 新增BMS设备
*
* @param bmsDevice BMS设备
* @return 结果
*/
int insertBmsDevice(BmsDevice bmsDevice);
/**
* 修改BMS设备
*
* @param bmsDevice BMS设备
* @return 结果
*/
int updateBmsDevice(BmsDevice bmsDevice);
/**
* 批量删除BMS设备
*
* @param deviceIds 需要删除的BMS设备主键集合
* @return 结果
*/
int deleteBmsDeviceByDeviceIds(Long[] deviceIds);
/**
* 删除BMS设备信息
*
* @param deviceId BMS设备主键
* @return 结果
*/
int deleteBmsDeviceByDeviceId(Long deviceId);
/**
* 根据设备编号查询设备信息
*
* @param deviceCode 设备编号
* @return BMS设备
*/
BmsDevice selectBmsDeviceByCode(String deviceCode);
/**
* 批量更新设备状态
*
* @param deviceIds 设备ID数组
* @param status 状态
* @return 结果
*/
int updateBmsDeviceStatus(Long[] deviceIds, String status);
}

View File

@ -0,0 +1,61 @@
package com.evobms.project.bms.service;
import java.util.List;
import com.evobms.project.bms.domain.BmsRuntimeData;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* BMS实时运行数据Service接口
*
* @author tzy
* @date 2026-02-23
*/
public interface IBmsRuntimeDataService extends IService<BmsRuntimeData> {
/**
* 查询BMS实时运行数据
*
* @param id BMS实时运行数据主键
* @return BMS实时运行数据
*/
public BmsRuntimeData selectBmsRuntimeDataById(Long id);
/**
* 查询BMS实时运行数据列表
*
* @param bmsRuntimeData BMS实时运行数据
* @return BMS实时运行数据集合
*/
public List<BmsRuntimeData> selectBmsRuntimeDataList(BmsRuntimeData bmsRuntimeData);
/**
* 新增BMS实时运行数据
*
* @param bmsRuntimeData BMS实时运行数据
* @return 结果
*/
public int insertBmsRuntimeData(BmsRuntimeData bmsRuntimeData);
/**
* 修改BMS实时运行数据
*
* @param bmsRuntimeData BMS实时运行数据
* @return 结果
*/
public int updateBmsRuntimeData(BmsRuntimeData bmsRuntimeData);
/**
* 批量删除BMS实时运行数据
*
* @param ids 需要删除的BMS实时运行数据主键集合
* @return 结果
*/
public int deleteBmsRuntimeDataByIds(Long[] ids);
/**
* 删除BMS实时运行数据信息
*
* @param id BMS实时运行数据主键
* @return 结果
*/
public int deleteBmsRuntimeDataById(Long id);
}

View File

@ -0,0 +1,61 @@
package com.evobms.project.bms.service;
import java.util.List;
import com.evobms.project.bms.domain.BmsSopData;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* BMS功率能力(SOP)数据Service接口
*
* @author tzy
* @date 2026-02-23
*/
public interface IBmsSopDataService extends IService<BmsSopData> {
/**
* 查询BMS功率能力(SOP)数据
*
* @param id BMS功率能力(SOP)数据主键
* @return BMS功率能力(SOP)数据
*/
public BmsSopData selectBmsSopDataById(Long id);
/**
* 查询BMS功率能力(SOP)数据列表
*
* @param bmsSopData BMS功率能力(SOP)数据
* @return BMS功率能力(SOP)数据集合
*/
public List<BmsSopData> selectBmsSopDataList(BmsSopData bmsSopData);
/**
* 新增BMS功率能力(SOP)数据
*
* @param bmsSopData BMS功率能力(SOP)数据
* @return 结果
*/
public int insertBmsSopData(BmsSopData bmsSopData);
/**
* 修改BMS功率能力(SOP)数据
*
* @param bmsSopData BMS功率能力(SOP)数据
* @return 结果
*/
public int updateBmsSopData(BmsSopData bmsSopData);
/**
* 批量删除BMS功率能力(SOP)数据
*
* @param ids 需要删除的BMS功率能力(SOP)数据主键集合
* @return 结果
*/
public int deleteBmsSopDataByIds(Long[] ids);
/**
* 删除BMS功率能力(SOP)数据信息
*
* @param id BMS功率能力(SOP)数据主键
* @return 结果
*/
public int deleteBmsSopDataById(Long id);
}

View File

@ -0,0 +1,61 @@
package com.evobms.project.bms.service;
import java.util.List;
import com.evobms.project.bms.domain.BmsSystemStatus;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* BMS系统状态数据Service接口
*
* @author tzy
* @date 2026-02-23
*/
public interface IBmsSystemStatusService extends IService<BmsSystemStatus> {
/**
* 查询BMS系统状态数据
*
* @param id BMS系统状态数据主键
* @return BMS系统状态数据
*/
public BmsSystemStatus selectBmsSystemStatusById(Long id);
/**
* 查询BMS系统状态数据列表
*
* @param bmsSystemStatus BMS系统状态数据
* @return BMS系统状态数据集合
*/
public List<BmsSystemStatus> selectBmsSystemStatusList(BmsSystemStatus bmsSystemStatus);
/**
* 新增BMS系统状态数据
*
* @param bmsSystemStatus BMS系统状态数据
* @return 结果
*/
public int insertBmsSystemStatus(BmsSystemStatus bmsSystemStatus);
/**
* 修改BMS系统状态数据
*
* @param bmsSystemStatus BMS系统状态数据
* @return 结果
*/
public int updateBmsSystemStatus(BmsSystemStatus bmsSystemStatus);
/**
* 批量删除BMS系统状态数据
*
* @param ids 需要删除的BMS系统状态数据主键集合
* @return 结果
*/
public int deleteBmsSystemStatusByIds(Long[] ids);
/**
* 删除BMS系统状态数据信息
*
* @param id BMS系统状态数据主键
* @return 结果
*/
public int deleteBmsSystemStatusById(Long id);
}

View File

@ -0,0 +1,61 @@
package com.evobms.project.bms.service;
import java.util.List;
import com.evobms.project.bms.domain.BmsVoltageChannelData;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* BMS电压通道数据Service接口
*
* @author tzy
* @date 2026-02-23
*/
public interface IBmsVoltageChannelDataService extends IService<BmsVoltageChannelData> {
/**
* 查询BMS电压通道数据
*
* @param id BMS电压通道数据主键
* @return BMS电压通道数据
*/
public BmsVoltageChannelData selectBmsVoltageChannelDataById(Long id);
/**
* 查询BMS电压通道数据列表
*
* @param bmsVoltageChannelData BMS电压通道数据
* @return BMS电压通道数据集合
*/
public List<BmsVoltageChannelData> selectBmsVoltageChannelDataList(BmsVoltageChannelData bmsVoltageChannelData);
/**
* 新增BMS电压通道数据
*
* @param bmsVoltageChannelData BMS电压通道数据
* @return 结果
*/
public int insertBmsVoltageChannelData(BmsVoltageChannelData bmsVoltageChannelData);
/**
* 修改BMS电压通道数据
*
* @param bmsVoltageChannelData BMS电压通道数据
* @return 结果
*/
public int updateBmsVoltageChannelData(BmsVoltageChannelData bmsVoltageChannelData);
/**
* 批量删除BMS电压通道数据
*
* @param ids 需要删除的BMS电压通道数据主键集合
* @return 结果
*/
public int deleteBmsVoltageChannelDataByIds(Long[] ids);
/**
* 删除BMS电压通道数据信息
*
* @param id BMS电压通道数据主键
* @return 结果
*/
public int deleteBmsVoltageChannelDataById(Long id);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,97 @@
package com.evobms.project.bms.service.impl;
import java.util.List;
import com.evobms.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evobms.project.bms.mapper.BmsChargeDetailMapper;
import com.evobms.project.bms.domain.BmsChargeDetail;
import com.evobms.project.bms.service.IBmsChargeDetailService;
/**
* BMS充电过程详细数据Service业务层处理
*
* @author tzy
* @date 2026-02-23
*/
@Service
public class BmsChargeDetailServiceImpl extends ServiceImpl<BmsChargeDetailMapper, BmsChargeDetail> implements IBmsChargeDetailService
{
@Autowired
private BmsChargeDetailMapper bmsChargeDetailMapper;
/**
* 查询BMS充电过程详细数据
*
* @param id BMS充电过程详细数据主键
* @return BMS充电过程详细数据
*/
@Override
public BmsChargeDetail selectBmsChargeDetailById(Long id)
{
return bmsChargeDetailMapper.selectBmsChargeDetailById(id);
}
/**
* 查询BMS充电过程详细数据列表
*
* @param bmsChargeDetail BMS充电过程详细数据
* @return BMS充电过程详细数据
*/
@Override
public List<BmsChargeDetail> selectBmsChargeDetailList(BmsChargeDetail bmsChargeDetail)
{
return bmsChargeDetailMapper.selectBmsChargeDetailList(bmsChargeDetail);
}
/**
* 新增BMS充电过程详细数据
*
* @param bmsChargeDetail BMS充电过程详细数据
* @return 结果
*/
@Override
public int insertBmsChargeDetail(BmsChargeDetail bmsChargeDetail)
{
bmsChargeDetail.setCreateTime(DateUtils.getNowDate());
return bmsChargeDetailMapper.insertBmsChargeDetail(bmsChargeDetail);
}
/**
* 修改BMS充电过程详细数据
*
* @param bmsChargeDetail BMS充电过程详细数据
* @return 结果
*/
@Override
public int updateBmsChargeDetail(BmsChargeDetail bmsChargeDetail)
{
bmsChargeDetail.setUpdateTime(DateUtils.getNowDate());
return bmsChargeDetailMapper.updateBmsChargeDetail(bmsChargeDetail);
}
/**
* 批量删除BMS充电过程详细数据
*
* @param ids 需要删除的BMS充电过程详细数据主键
* @return 结果
*/
@Override
public int deleteBmsChargeDetailByIds(Long[] ids)
{
return bmsChargeDetailMapper.deleteBmsChargeDetailByIds(ids);
}
/**
* 删除BMS充电过程详细数据信息
*
* @param id BMS充电过程详细数据主键
* @return 结果
*/
@Override
public int deleteBmsChargeDetailById(Long id)
{
return bmsChargeDetailMapper.deleteBmsChargeDetailById(id);
}
}

View File

@ -0,0 +1,97 @@
package com.evobms.project.bms.service.impl;
import java.util.List;
import com.evobms.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evobms.project.bms.mapper.BmsChargeTemperatureMapper;
import com.evobms.project.bms.domain.BmsChargeTemperature;
import com.evobms.project.bms.service.IBmsChargeTemperatureService;
/**
* BMS充电座温度数据Service业务层处理
*
* @author tzy
* @date 2026-02-23
*/
@Service
public class BmsChargeTemperatureServiceImpl extends ServiceImpl<BmsChargeTemperatureMapper, BmsChargeTemperature> implements IBmsChargeTemperatureService
{
@Autowired
private BmsChargeTemperatureMapper bmsChargeTemperatureMapper;
/**
* 查询BMS充电座温度数据
*
* @param id BMS充电座温度数据主键
* @return BMS充电座温度数据
*/
@Override
public BmsChargeTemperature selectBmsChargeTemperatureById(Long id)
{
return bmsChargeTemperatureMapper.selectBmsChargeTemperatureById(id);
}
/**
* 查询BMS充电座温度数据列表
*
* @param bmsChargeTemperature BMS充电座温度数据
* @return BMS充电座温度数据
*/
@Override
public List<BmsChargeTemperature> selectBmsChargeTemperatureList(BmsChargeTemperature bmsChargeTemperature)
{
return bmsChargeTemperatureMapper.selectBmsChargeTemperatureList(bmsChargeTemperature);
}
/**
* 新增BMS充电座温度数据
*
* @param bmsChargeTemperature BMS充电座温度数据
* @return 结果
*/
@Override
public int insertBmsChargeTemperature(BmsChargeTemperature bmsChargeTemperature)
{
bmsChargeTemperature.setCreateTime(DateUtils.getNowDate());
return bmsChargeTemperatureMapper.insertBmsChargeTemperature(bmsChargeTemperature);
}
/**
* 修改BMS充电座温度数据
*
* @param bmsChargeTemperature BMS充电座温度数据
* @return 结果
*/
@Override
public int updateBmsChargeTemperature(BmsChargeTemperature bmsChargeTemperature)
{
bmsChargeTemperature.setUpdateTime(DateUtils.getNowDate());
return bmsChargeTemperatureMapper.updateBmsChargeTemperature(bmsChargeTemperature);
}
/**
* 批量删除BMS充电座温度数据
*
* @param ids 需要删除的BMS充电座温度数据主键
* @return 结果
*/
@Override
public int deleteBmsChargeTemperatureByIds(Long[] ids)
{
return bmsChargeTemperatureMapper.deleteBmsChargeTemperatureByIds(ids);
}
/**
* 删除BMS充电座温度数据信息
*
* @param id BMS充电座温度数据主键
* @return 结果
*/
@Override
public int deleteBmsChargeTemperatureById(Long id)
{
return bmsChargeTemperatureMapper.deleteBmsChargeTemperatureById(id);
}
}

View File

@ -1,115 +0,0 @@
package com.evobms.project.bms.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evobms.common.utils.DateUtils;
import com.evobms.project.bms.domain.BmsDevice;
import com.evobms.project.bms.mapper.BmsDeviceMapper;
import com.evobms.project.bms.service.IBmsDeviceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* BMS设备Service业务层处理
*
* @author evobms
* @date 2025-01-22
*/
@Service
public class BmsDeviceServiceImpl extends ServiceImpl<BmsDeviceMapper, BmsDevice> implements IBmsDeviceService {
@Autowired
private BmsDeviceMapper bmsDeviceMapper;
/**
* 查询BMS设备
*
* @param deviceId BMS设备主键
* @return BMS设备
*/
@Override
public BmsDevice selectBmsDeviceByDeviceId(Long deviceId) {
return bmsDeviceMapper.selectById(deviceId);
}
/**
* 查询BMS设备列表
*
* @param bmsDevice BMS设备
* @return BMS设备
*/
@Override
public List<BmsDevice> selectBmsDeviceList(BmsDevice bmsDevice) {
return bmsDeviceMapper.selectBmsDeviceList(bmsDevice);
}
/**
* 新增BMS设备
*
* @param bmsDevice BMS设备
* @return 结果
*/
@Override
public int insertBmsDevice(BmsDevice bmsDevice) {
bmsDevice.setCreateTime(DateUtils.getNowDate());
return bmsDeviceMapper.insert(bmsDevice);
}
/**
* 修改BMS设备
*
* @param bmsDevice BMS设备
* @return 结果
*/
@Override
public int updateBmsDevice(BmsDevice bmsDevice) {
bmsDevice.setUpdateTime(DateUtils.getNowDate());
return bmsDeviceMapper.updateById(bmsDevice);
}
/**
* 批量删除BMS设备
*
* @param deviceIds 需要删除的BMS设备主键
* @return 结果
*/
@Override
public int deleteBmsDeviceByDeviceIds(Long[] deviceIds) {
return bmsDeviceMapper.deleteBatchIds(java.util.Arrays.asList(deviceIds));
}
/**
* 删除BMS设备信息
*
* @param deviceId BMS设备主键
* @return 结果
*/
@Override
public int deleteBmsDeviceByDeviceId(Long deviceId) {
return bmsDeviceMapper.deleteById(deviceId);
}
/**
* 根据设备编号查询设备信息
*
* @param deviceCode 设备编号
* @return BMS设备
*/
@Override
public BmsDevice selectBmsDeviceByCode(String deviceCode) {
return bmsDeviceMapper.selectBmsDeviceByCode(deviceCode);
}
/**
* 批量更新设备状态
*
* @param deviceIds 设备ID数组
* @param status 状态
* @return 结果
*/
@Override
public int updateBmsDeviceStatus(Long[] deviceIds, String status) {
return bmsDeviceMapper.updateBmsDeviceStatus(deviceIds, status);
}
}

View File

@ -0,0 +1,97 @@
package com.evobms.project.bms.service.impl;
import java.util.List;
import com.evobms.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evobms.project.bms.mapper.BmsRuntimeDataMapper;
import com.evobms.project.bms.domain.BmsRuntimeData;
import com.evobms.project.bms.service.IBmsRuntimeDataService;
/**
* BMS实时运行数据Service业务层处理
*
* @author tzy
* @date 2026-02-23
*/
@Service
public class BmsRuntimeDataServiceImpl extends ServiceImpl<BmsRuntimeDataMapper, BmsRuntimeData> implements IBmsRuntimeDataService
{
@Autowired
private BmsRuntimeDataMapper bmsRuntimeDataMapper;
/**
* 查询BMS实时运行数据
*
* @param id BMS实时运行数据主键
* @return BMS实时运行数据
*/
@Override
public BmsRuntimeData selectBmsRuntimeDataById(Long id)
{
return bmsRuntimeDataMapper.selectBmsRuntimeDataById(id);
}
/**
* 查询BMS实时运行数据列表
*
* @param bmsRuntimeData BMS实时运行数据
* @return BMS实时运行数据
*/
@Override
public List<BmsRuntimeData> selectBmsRuntimeDataList(BmsRuntimeData bmsRuntimeData)
{
return bmsRuntimeDataMapper.selectBmsRuntimeDataList(bmsRuntimeData);
}
/**
* 新增BMS实时运行数据
*
* @param bmsRuntimeData BMS实时运行数据
* @return 结果
*/
@Override
public int insertBmsRuntimeData(BmsRuntimeData bmsRuntimeData)
{
bmsRuntimeData.setCreateTime(DateUtils.getNowDate());
return bmsRuntimeDataMapper.insertBmsRuntimeData(bmsRuntimeData);
}
/**
* 修改BMS实时运行数据
*
* @param bmsRuntimeData BMS实时运行数据
* @return 结果
*/
@Override
public int updateBmsRuntimeData(BmsRuntimeData bmsRuntimeData)
{
bmsRuntimeData.setUpdateTime(DateUtils.getNowDate());
return bmsRuntimeDataMapper.updateBmsRuntimeData(bmsRuntimeData);
}
/**
* 批量删除BMS实时运行数据
*
* @param ids 需要删除的BMS实时运行数据主键
* @return 结果
*/
@Override
public int deleteBmsRuntimeDataByIds(Long[] ids)
{
return bmsRuntimeDataMapper.deleteBmsRuntimeDataByIds(ids);
}
/**
* 删除BMS实时运行数据信息
*
* @param id BMS实时运行数据主键
* @return 结果
*/
@Override
public int deleteBmsRuntimeDataById(Long id)
{
return bmsRuntimeDataMapper.deleteBmsRuntimeDataById(id);
}
}

View File

@ -0,0 +1,97 @@
package com.evobms.project.bms.service.impl;
import java.util.List;
import com.evobms.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evobms.project.bms.mapper.BmsSopDataMapper;
import com.evobms.project.bms.domain.BmsSopData;
import com.evobms.project.bms.service.IBmsSopDataService;
/**
* BMS功率能力(SOP)数据Service业务层处理
*
* @author tzy
* @date 2026-02-23
*/
@Service
public class BmsSopDataServiceImpl extends ServiceImpl<BmsSopDataMapper, BmsSopData> implements IBmsSopDataService
{
@Autowired
private BmsSopDataMapper bmsSopDataMapper;
/**
* 查询BMS功率能力(SOP)数据
*
* @param id BMS功率能力(SOP)数据主键
* @return BMS功率能力(SOP)数据
*/
@Override
public BmsSopData selectBmsSopDataById(Long id)
{
return bmsSopDataMapper.selectBmsSopDataById(id);
}
/**
* 查询BMS功率能力(SOP)数据列表
*
* @param bmsSopData BMS功率能力(SOP)数据
* @return BMS功率能力(SOP)数据
*/
@Override
public List<BmsSopData> selectBmsSopDataList(BmsSopData bmsSopData)
{
return bmsSopDataMapper.selectBmsSopDataList(bmsSopData);
}
/**
* 新增BMS功率能力(SOP)数据
*
* @param bmsSopData BMS功率能力(SOP)数据
* @return 结果
*/
@Override
public int insertBmsSopData(BmsSopData bmsSopData)
{
bmsSopData.setCreateTime(DateUtils.getNowDate());
return bmsSopDataMapper.insertBmsSopData(bmsSopData);
}
/**
* 修改BMS功率能力(SOP)数据
*
* @param bmsSopData BMS功率能力(SOP)数据
* @return 结果
*/
@Override
public int updateBmsSopData(BmsSopData bmsSopData)
{
bmsSopData.setUpdateTime(DateUtils.getNowDate());
return bmsSopDataMapper.updateBmsSopData(bmsSopData);
}
/**
* 批量删除BMS功率能力(SOP)数据
*
* @param ids 需要删除的BMS功率能力(SOP)数据主键
* @return 结果
*/
@Override
public int deleteBmsSopDataByIds(Long[] ids)
{
return bmsSopDataMapper.deleteBmsSopDataByIds(ids);
}
/**
* 删除BMS功率能力(SOP)数据信息
*
* @param id BMS功率能力(SOP)数据主键
* @return 结果
*/
@Override
public int deleteBmsSopDataById(Long id)
{
return bmsSopDataMapper.deleteBmsSopDataById(id);
}
}

View File

@ -0,0 +1,97 @@
package com.evobms.project.bms.service.impl;
import java.util.List;
import com.evobms.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evobms.project.bms.mapper.BmsSystemStatusMapper;
import com.evobms.project.bms.domain.BmsSystemStatus;
import com.evobms.project.bms.service.IBmsSystemStatusService;
/**
* BMS系统状态数据Service业务层处理
*
* @author tzy
* @date 2026-02-23
*/
@Service
public class BmsSystemStatusServiceImpl extends ServiceImpl<BmsSystemStatusMapper, BmsSystemStatus> implements IBmsSystemStatusService
{
@Autowired
private BmsSystemStatusMapper bmsSystemStatusMapper;
/**
* 查询BMS系统状态数据
*
* @param id BMS系统状态数据主键
* @return BMS系统状态数据
*/
@Override
public BmsSystemStatus selectBmsSystemStatusById(Long id)
{
return bmsSystemStatusMapper.selectBmsSystemStatusById(id);
}
/**
* 查询BMS系统状态数据列表
*
* @param bmsSystemStatus BMS系统状态数据
* @return BMS系统状态数据
*/
@Override
public List<BmsSystemStatus> selectBmsSystemStatusList(BmsSystemStatus bmsSystemStatus)
{
return bmsSystemStatusMapper.selectBmsSystemStatusList(bmsSystemStatus);
}
/**
* 新增BMS系统状态数据
*
* @param bmsSystemStatus BMS系统状态数据
* @return 结果
*/
@Override
public int insertBmsSystemStatus(BmsSystemStatus bmsSystemStatus)
{
bmsSystemStatus.setCreateTime(DateUtils.getNowDate());
return bmsSystemStatusMapper.insertBmsSystemStatus(bmsSystemStatus);
}
/**
* 修改BMS系统状态数据
*
* @param bmsSystemStatus BMS系统状态数据
* @return 结果
*/
@Override
public int updateBmsSystemStatus(BmsSystemStatus bmsSystemStatus)
{
bmsSystemStatus.setUpdateTime(DateUtils.getNowDate());
return bmsSystemStatusMapper.updateBmsSystemStatus(bmsSystemStatus);
}
/**
* 批量删除BMS系统状态数据
*
* @param ids 需要删除的BMS系统状态数据主键
* @return 结果
*/
@Override
public int deleteBmsSystemStatusByIds(Long[] ids)
{
return bmsSystemStatusMapper.deleteBmsSystemStatusByIds(ids);
}
/**
* 删除BMS系统状态数据信息
*
* @param id BMS系统状态数据主键
* @return 结果
*/
@Override
public int deleteBmsSystemStatusById(Long id)
{
return bmsSystemStatusMapper.deleteBmsSystemStatusById(id);
}
}

View File

@ -0,0 +1,97 @@
package com.evobms.project.bms.service.impl;
import java.util.List;
import com.evobms.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evobms.project.bms.mapper.BmsVoltageChannelDataMapper;
import com.evobms.project.bms.domain.BmsVoltageChannelData;
import com.evobms.project.bms.service.IBmsVoltageChannelDataService;
/**
* BMS电压通道数据Service业务层处理
*
* @author tzy
* @date 2026-02-23
*/
@Service
public class BmsVoltageChannelDataServiceImpl extends ServiceImpl<BmsVoltageChannelDataMapper, BmsVoltageChannelData> implements IBmsVoltageChannelDataService
{
@Autowired
private BmsVoltageChannelDataMapper bmsVoltageChannelDataMapper;
/**
* 查询BMS电压通道数据
*
* @param id BMS电压通道数据主键
* @return BMS电压通道数据
*/
@Override
public BmsVoltageChannelData selectBmsVoltageChannelDataById(Long id)
{
return bmsVoltageChannelDataMapper.selectBmsVoltageChannelDataById(id);
}
/**
* 查询BMS电压通道数据列表
*
* @param bmsVoltageChannelData BMS电压通道数据
* @return BMS电压通道数据
*/
@Override
public List<BmsVoltageChannelData> selectBmsVoltageChannelDataList(BmsVoltageChannelData bmsVoltageChannelData)
{
return bmsVoltageChannelDataMapper.selectBmsVoltageChannelDataList(bmsVoltageChannelData);
}
/**
* 新增BMS电压通道数据
*
* @param bmsVoltageChannelData BMS电压通道数据
* @return 结果
*/
@Override
public int insertBmsVoltageChannelData(BmsVoltageChannelData bmsVoltageChannelData)
{
bmsVoltageChannelData.setCreateTime(DateUtils.getNowDate());
return bmsVoltageChannelDataMapper.insertBmsVoltageChannelData(bmsVoltageChannelData);
}
/**
* 修改BMS电压通道数据
*
* @param bmsVoltageChannelData BMS电压通道数据
* @return 结果
*/
@Override
public int updateBmsVoltageChannelData(BmsVoltageChannelData bmsVoltageChannelData)
{
bmsVoltageChannelData.setUpdateTime(DateUtils.getNowDate());
return bmsVoltageChannelDataMapper.updateBmsVoltageChannelData(bmsVoltageChannelData);
}
/**
* 批量删除BMS电压通道数据
*
* @param ids 需要删除的BMS电压通道数据主键
* @return 结果
*/
@Override
public int deleteBmsVoltageChannelDataByIds(Long[] ids)
{
return bmsVoltageChannelDataMapper.deleteBmsVoltageChannelDataByIds(ids);
}
/**
* 删除BMS电压通道数据信息
*
* @param id BMS电压通道数据主键
* @return 结果
*/
@Override
public int deleteBmsVoltageChannelDataById(Long id)
{
return bmsVoltageChannelDataMapper.deleteBmsVoltageChannelDataById(id);
}
}

View File

@ -9,6 +9,18 @@ import com.evobms.project.iot.service.DeviceTokenService;
import com.evobms.project.system.domain.BmsDevices;
import com.evobms.project.system.service.IBmsDevicesService;
import com.evobms.project.ota.service.IOtaTasksService;
import com.evobms.project.ota.domain.OtaTasks;
import com.evobms.project.iot.domain.dto.OtaUploadDto;
import com.evobms.project.iot.domain.dto.OtaLoginDto;
import com.evobms.project.iot.domain.dto.OtaHistoryResultDTO;
import com.evobms.project.iot.domain.dto.OtaProgressResultDTO;
import com.evobms.project.iot.domain.dto.OtaUploadResultDTO;
import org.springframework.web.multipart.MultipartFile;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
@ -25,8 +37,10 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
@ -36,19 +50,18 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.SecureRandom;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* 设备鉴权令牌接口
* 路径POST /iot/auth/token
*/
@RestController
@Tag(name = "设备鉴权", description = "设备鉴权令牌接口")
@Api(tags = "IoT设备鉴权")
@RequestMapping("/iot")
@Slf4j
public class IotAuthController {
private static final Logger log = LoggerFactory.getLogger(IotAuthController.class);
@Autowired(required = false)
private IBmsDevicesService bmsDevicesService;
@ -59,9 +72,13 @@ public class IotAuthController {
@Value("${token.header:Authorization}")
private String tokenHeader;
@Value("${mqtt.host:tcp://localhost:1883}")
@Value("${mqtt.host:tcp://61.182.73.218:11883}")
private String mqttHostUri;
@Value("${mqtt.username:00000001}")
@Value("${mqtt.publicHost:61.182.73.218}")
private String mqttPublicHost;
@Value("${mqtt.publicPort:11883}")
private int mqttPublicPort;
@Value("${mqtt.username:`00000001`}")
private String mqttUsername;
@Value("${mqtt.password:741a03a10f3de6b2}")
private String mqttPassword;
@ -73,7 +90,7 @@ public class IotAuthController {
private String otaCount;
// OTA文件存储位置支持 classpath 文件系统目录例如classpath:/ota /data/ota
@Value("${ota.repo:classpath:/ota}")
@Value("${ota.repo:./ota}")
private String otaRepo;
// OTA分片大小字节规范为1KB=1024字节
@ -89,11 +106,14 @@ public class IotAuthController {
* 返回{ code, success, token }
*/
@PostMapping("/auth/token")
@Operation(summary = "设备请求令牌", description = "通过SN码设备号版本信息生成TOKEN")
@ApiOperation("设备请求令牌")
public Map<String, Object> requestToken(@RequestBody Map<String, String> body) {
String sn = safeTrim(body.get("sn"));
String model = safeTrim(body.get("device"));
String version = safeTrim(body.get("version"));
log.info("设备请求:{}",body.toString());
log.info("sn:{}, device:{}, version:{}", sn, model, version);
MDC.put("sn", sn);
MDC.put("model", model);
MDC.put("version", version);
@ -113,10 +133,8 @@ public class IotAuthController {
BmsDevices query = new BmsDevices();
// 依据设备上报的SN使用系统侧BmsDevices的deviceSn字段进行校验
query.setDeviceSn(sn);
List<BmsDevices> list = bmsDevicesService.selectBmsDevicesList(query);
if (list != null && !list.isEmpty()) {
device = list.get(0);
}
device = bmsDevicesService.selectBmsDevicesList1(query);
}
if (device == null) {
@ -133,7 +151,7 @@ public class IotAuthController {
return resp;
}
// 设备合法改为自定义非JWT令牌长度固定65
// 设备合法改为JWT令牌长度固定65
String token = deviceTokenService.issueTokenForSn(sn);
log.info("BBOX设备token: {}", token);
log.info("设备令牌签发成功: sn={} model={} version={} tokenLen={}", sn, model, version, token != null ? token.length() : 0);
@ -162,6 +180,8 @@ public class IotAuthController {
* 请求参数version可选
* 返回{ code, success, data(Base64) }
*/
@Operation(summary = " MQTT 登录", description = "获取设备端 MQTT 登录信息")
@ApiOperation(value = "MQTT 登录", notes = "获取设备端 MQTT 登录信息")
@GetMapping("/auth/mqtt")
public Map<String, Object> getMqttCredentials(@RequestParam(value = "version", required = false) String version,
HttpServletRequest request) {
@ -175,21 +195,24 @@ public class IotAuthController {
return resp;
}
// 解析 MQTT 主机与端口
String host = "61.182.73.218";
int port = 1883;
try {
URI uri = new URI(mqttHostUri);
if (uri.getHost() != null) {
host = uri.getHost();
String host = safeTrim(mqttPublicHost);
Integer port = mqttPublicPort > 0 ? mqttPublicPort : null;
if (StringUtils.isEmpty(host) || port == null) {
try {
URI uri = new URI(mqttHostUri);
if (StringUtils.isEmpty(host) && uri.getHost() != null && !uri.getHost().isEmpty()) {
host = uri.getHost();
}
if (port == null && uri.getPort() != -1) {
port = uri.getPort();
}
} catch (Exception ignore) {
}
if (uri.getPort() != -1) {
port = uri.getPort();
}
} catch (Exception ignore) {}
}
if (port == null || port <= 0) port = 11883;
String use = fixLengthRight(mqttUsername, 8);
String pass = fixLengthRight(mqttPassword,16);
String pass = fixLengthRight(mqttPassword, 16);
Map<String, String> data = new HashMap<>();
data.put("clientid", sn);
@ -199,10 +222,20 @@ public class IotAuthController {
data.put("passwd", pass);
String json = JSON.toJSONString(data);
log.debug("登录mqtt信息====》{}",json);
String key16 = fixLengthRight(sn, 16); // AES密钥sn后补0到16位
String encryptedBase64 = AesEcbPkcs5Utils.encryptToAscII16(json, key16);
log.debug("登录mqtt信息加密===================》{}",encryptedBase64);
log.debug("登录mqtt信息====》{}", json);
// AES密钥sn后补0到16位
String encryptedBase64;
if (sn.equals("EVO0001")) {
String key161 = fixLengthRight("EVO0001", 16);
encryptedBase64 = AesEcbPkcs5Utils.encryptToAscII16(json, key161);
} else if (sn.equals("EVO0002")) {
String key161 = fixLengthRight("EVO0002", 16);
encryptedBase64 = AesEcbPkcs5Utils.encryptToAscII16(json, key161);
}else {
String keyTail6 = sn.length() >= 16 ? sn.substring(sn.length() - 16) : sn;
encryptedBase64 = AesEcbPkcs5Utils.encryptToAscII16(json, keyTail6);
}
log.debug("登录mqtt信息加密===================》{}", encryptedBase64);
resp.put("code", BboxApiConstants.CODE_SUCCESS);
resp.put("success", "1");
resp.put("data", encryptedBase64);
@ -221,6 +254,7 @@ public class IotAuthController {
* 请求参数(body){ "version":"1.0.0" }
* 返回{ code(3位), success(1位), update(1位), version(5位), url(11位), count(3位) }
*/
@ApiOperation(value = "设备登录", notes = "设备登录并上报固件版本服务端判断是否需要OTA升级")
@PostMapping("/auth/login")
public Map<String, Object> deviceLogin(@RequestBody(required = false) String body, @RequestParam(value = "version", required = false) String versionParam, HttpServletRequest request) {
Map<String, Object> resp = new HashMap<>();
@ -270,10 +304,8 @@ public class IotAuthController {
if (bmsDevicesService != null) {
BmsDevices query = new BmsDevices();
query.setDeviceSn(sn);
List<BmsDevices> list = bmsDevicesService.selectBmsDevicesList(query);
if (list != null && !list.isEmpty()) {
device = list.get(0);
}
device = bmsDevicesService.selectBmsDevicesList1(query);
}
if (device == null) {
@ -285,7 +317,8 @@ public class IotAuthController {
// 目标升级版本直接取设备记录中的固件版本跳过 OTA 任务查询
String targetVersion = null;
try { targetVersion = device.getFirmwareVersion();
try {
targetVersion = device.getFirmwareVersion();
} catch (Exception ignore) {
}
@ -310,7 +343,7 @@ public class IotAuthController {
// 若存在目标版本对应固件可用时动态计算分片总数总长度含 64 字节校验头否则回退配置值
Integer dynamicCount = computeOtaChunkCount(targetVersion);
if (dynamicCount != null && dynamicCount > 0) {
// 转为 3 位字符串左侧补零例如 7 -> "007"
// 转为 3 位字符串左侧补零 7 -> "007"
count = fixLengthLeft(String.valueOf(dynamicCount), 3);
} else {
// 回退使用配置 `ota.count`默认 "001"
@ -362,13 +395,15 @@ public class IotAuthController {
* - 路径`/iot/ota/{num}`num 为三位数字字符串000 起始
* - 参数`version`必填目标固件版本号例如 `1.0.1`
* - 响应
* 成功`application/octet-stream` 二进制数据每次返回最多 1024 字节最后一片可能不足 1024
* 失败`{ code, success }` JSON错误码沿用 `BboxApiConstants`
* 成功`application/octet-stream` 二进制数据每次返回最多 1024 字节最后一片可能不足 1024
* 失败`{ code, success }` JSON错误码沿用 `BboxApiConstants`
* - 首片摘要计算针对首片1024字节数据生成摘要并作为校验头32字节SHA-256原始摘要
* - 首片结构`[header] + [首片数据(最多1024)]`总长 `headerLen + 1024`
* - 后续分片与首片相同每片前置相同模式的校验头摘要基于该分片的数据随后紧跟该分片数据
*/
@GetMapping("/ota/{num}")
@Operation(summary = "OTA固件分片下载", description = " OTA 固件分片下载接口")
@ApiOperation("OTA固件分片下载")
public Object downloadOtaChunk(@PathVariable("num") String num,
@RequestParam(value = "version", required = false) String version,
HttpServletRequest request) {
@ -425,7 +460,7 @@ public class IotAuthController {
try {
int previewLen = Math.min(otaChunkSize, firmwareBytes);
byte[] preview = Arrays.copyOfRange(firmware, 0, previewLen);
// 变更说明headerSHA256日志打印为首片1024字节数据的摘要而非整包
// headerSHA256日志打印为首片1024字节数据的摘要而非整包
String headerHex = sha256Hex(preview);
log.info("OTA下载开始: sn={} version={} headerSHA256={} headerMode={} chunkSize={} firmwareBytes={} chunks={}", sn, version, headerHex, "binary32", otaChunkSize, firmwareBytes, chunks);
} catch (Exception ignore) {
@ -453,9 +488,10 @@ public class IotAuthController {
log.info("OTA首片头HEX({}): {}", headerLen, toHex(header));
int prefixLen = Math.min(64, firstData.length);
log.info("OTA首片数据HEX前{}字节: {}", prefixLen, toHex(Arrays.copyOfRange(firstData, 0, prefixLen)));
} catch (Exception ignore) {}
} catch (Exception ignore) {
}
} else {
// 后续分片从首片实际数据(1024)之后开始切片并在分片前置校验头摘要基于该分片数据
// 从首片实际数据(1024)之后开始切片并在分片前置校验头摘要基于该分片数据
int firstDataLen = Math.min(otaChunkSize, firmwareBytes);
int offset = firstDataLen + (idx - 1) * otaChunkSize;
if (offset >= firmwareBytes) {
@ -486,25 +522,76 @@ public class IotAuthController {
log.info("OTA分片头HEX({}): {}", headerLen, toHex(header));
int prefixLen = Math.min(64, dataForDigest.length);
log.info("OTA分片数据HEX前{}字节: {}", prefixLen, toHex(Arrays.copyOfRange(dataForDigest, 0, prefixLen)));
} catch (Exception ignore) {}
}
} catch (Exception ignore) {
}
}
log.debug("OTA分片发送: sn={} version={} num={} chunkLen={}",
sn, version, num, chunk.length);
double progress = (idx + 1) * 100.0 / Math.max(chunks, 1);
try {
if (otaTasksService != null) {
String deviceId = sn;
if (bmsDevicesService != null) {
try {
BmsDevices q = new BmsDevices();
q.setDeviceSn(sn);
BmsDevices dev = bmsDevicesService.selectBmsDevicesList1(q);
if (dev != null && dev.getDeviceId() != null) {
deviceId = dev.getDeviceId();
}
} catch (Exception ignore) {}
}
OtaTasks filter = new OtaTasks();
filter.setDeviceId(deviceId);
filter.setFirmwareVersion(version);
List<OtaTasks> list = otaTasksService.selectOtaTasksList(filter);
OtaTasks task = (list != null && !list.isEmpty()) ? list.get(0) : new OtaTasks();
boolean isNew = (task.getId() == null || task.getId().isEmpty());
if (isNew) {
task.setDeviceId(deviceId);
task.setTaskName("OTA-" + version);
task.setFirmwareVersion(version);
task.setStatus("IN_PROGRESS");
task.setProgress((long) Math.round((float) progress));
task.setStartTime(new Date());
task.setCreateBy(sn);
otaTasksService.insertOtaTasks(task);
} else {
task.setProgress((long) Math.round((float) progress));
if (idx == chunks - 1) {
task.setStatus("DONE");
task.setEndTime(new java.util.Date());
} else {
task.setStatus("IN_PROGRESS");
}
task.setUpdateTime(new java.util.Date());
otaTasksService.updateOtaTasks(task);
}
}
} catch (Exception ex) {
log.warn("OTA任务记录更新失败: sn={} version={} err={}", sn, version, ex.getMessage());
}
return ResponseEntity
.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.contentLength(chunk.length)
.header("X-OTA-Chunk-Index", String.valueOf(idx))
.header("X-OTA-Chunk-Total", String.valueOf(chunks))
.header("X-OTA-Progress", String.format(java.util.Locale.ROOT, "%.2f", progress))
.header("X-OTA-Chunk-Size", String.valueOf(otaChunkSize))
.header("X-OTA-Firmware-Bytes", String.valueOf(firmwareBytes))
.header("X-OTA-Mode", mode)
.body(chunk);
} catch (Exception e) {
log.error("OTA分片下载异常: {}", e.getMessage(), e);
err.put("code", BboxApiConstants.CODE_UNAUTHORIZED);
err.put("success", "0");
return err;
} finally {
MDC.clear();
log.error("OTA分片下载异常: {}", e.getMessage(), e);
err.put("code", BboxApiConstants.CODE_UNAUTHORIZED);
err.put("success", "0");
return err;
} finally {
MDC.clear();
}
}
}
/**
* 动态计算 OTA 分片总数
@ -525,6 +612,240 @@ public class IotAuthController {
}
}
@PostMapping(value = "/ota/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ApiOperation(value = "上传OTA固件", notes = "上传固件到 ota.repo 指定目录,保存为 <version>.bin", response = OtaUploadResultDTO.class)
public OtaUploadResultDTO uploadOta(@RequestParam("version") String version, @RequestParam("file") MultipartFile file) {
OtaUploadResultDTO resp = new OtaUploadResultDTO();
try {
if (StringUtils.isEmpty(version) || file == null || file.isEmpty()) {
resp.setCode(BboxApiConstants.CODE_PARAM_INVALID);
resp.setSuccess("0");
resp.setMsg("version或file缺失");
return resp;
}
if (otaRepo.startsWith("classpath:")) {
resp.setCode(BboxApiConstants.CODE_PARAM_INVALID);
resp.setSuccess("0");
resp.setMsg("classpath仓库不支持上传请配置文件系统路径");
return resp;
}
Path dir = Paths.get(otaRepo);
if (!Files.exists(dir)) {
Files.createDirectories(dir);
}
Path target = dir.resolve(version + ".bin");
Files.copy(file.getInputStream(), target, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
resp.setCode(BboxApiConstants.CODE_SUCCESS);
resp.setSuccess("1");
resp.setPath(target.toString());
resp.setSize(Files.size(target));
log.info("OTA上传成功: version={} repo={} target={} size={}", version, otaRepo, target.toString(), resp.getSize());
// 返回分片元信息
Integer chunks = computeOtaChunkCount(version);
resp.setChunks(chunks);
resp.setChunkSize(otaChunkSize);
return resp;
} catch (IOException e) {
resp.setCode(BboxApiConstants.CODE_FILE_NOT_FOUND);
resp.setSuccess("0");
resp.setMsg(e.getMessage());
return resp;
}
}
@GetMapping("/ota/meta")
@ApiOperation(value = "查询OTA元信息", notes = "返回分片总数、分片大小、固件字节数")
public Map<String, Object> otaMeta(@RequestParam("version") String version) {
Map<String, Object> resp = new HashMap<>();
if (StringUtils.isEmpty(version)) {
resp.put("code", BboxApiConstants.CODE_PARAM_INVALID);
resp.put("success", "0");
return resp;
}
byte[] fw = loadFirmwareBytes(version);
if (fw == null) {
resp.put("code", BboxApiConstants.CODE_FILE_NOT_FOUND);
resp.put("success", "0");
return resp;
}
resp.put("code", BboxApiConstants.CODE_SUCCESS);
resp.put("success", "1");
resp.put("firmwareBytes", fw.length);
resp.put("chunkSize", otaChunkSize);
resp.put("chunks", (fw.length + otaChunkSize - 1) / otaChunkSize);
return resp;
}
@GetMapping("/ota/history")
@ApiOperation(value = "OTA操作历史", notes = "前端用于展示操作历史列表sn为空时可从Authorization解析")
public OtaHistoryResultDTO otaHistory(@RequestParam(value = "sn", required = false) String sn,
@RequestParam(value = "version", required = false) String version,
@RequestParam(value = "limit", required = false) Integer limit,
HttpServletRequest request) {
OtaHistoryResultDTO out = new OtaHistoryResultDTO();
String snx = safeTrim(sn);
if (StringUtils.isEmpty(snx)) {
try {
snx = deviceTokenService.resolveSn(request);
} catch (Exception ignore) {
}
}
if (StringUtils.isEmpty(snx)) {
out.setCode(BboxApiConstants.CODE_PARAM_INVALID);
out.setSuccess("0");
out.setMsg("sn不能为空");
return out;
}
if (otaTasksService == null) {
out.setCode(BboxApiConstants.CODE_NOT_FOUND);
out.setSuccess("0");
out.setMsg("otaTasksService未启用");
out.setSn(snx);
return out;
}
String deviceId = resolveDeviceIdBySn(snx);
OtaTasks filter = new OtaTasks();
filter.setDeviceId(deviceId);
if (StringUtils.isNotEmpty(version)) {
filter.setFirmwareVersion(version);
}
List<OtaTasks> list = otaTasksService.selectOtaTasksList(filter);
List<OtaTasks> sorted = list == null ? new ArrayList<>() : new ArrayList<>(list);
sorted.sort(Comparator.comparing(this::resolveOtaSortTime, Comparator.nullsLast(Date::compareTo)).reversed());
int lim = (limit == null || limit <= 0) ? 50 : limit;
if (sorted.size() > lim) {
sorted = new ArrayList<>(sorted.subList(0, lim));
}
out.setCode(BboxApiConstants.CODE_SUCCESS);
out.setSuccess("1");
out.setMsg(BboxApiConstants.MSG_SUCCESS);
out.setSn(snx);
out.setDeviceId(deviceId);
out.setTaskList(sorted);
return out;
}
/**
* OTA 任务进度管理端轮询
* - 路径GET /iot/ota/progress
* - 参数sn可选缺省时从 Authorization 解析设备version可选缺省时取该设备最新任务
* - 说明`progress` 为任务整体 0100`totalChunks`/`chunkSize` 与设备登录 `count`分片下载逻辑对齐
* `downloadedChunks` `progress` `totalChunks` 推算用于可选的分片进度文案与浏览器上传到 /iot/ota/upload 无关
* - 响应示例
* <pre>
* {
* "code": 200,
* "success": "1",
* "msg": "success",
* "sn": "137PBP1M11001AFCF0000007",
* "deviceId": "BAT_001",
* "firmwareVersion": "1.0.1",
* "status": "IN_PROGRESS",
* "progress": 45,
* "totalChunks": 120,
* "downloadedChunks": 54,
* "chunkSize": 1024,
* "startTime": "2026-05-11 10:00:00",
* "endTime": null,
* "updateTime": "2026-05-11 10:05:00"
* }
* </pre>
*/
@GetMapping("/ota/progress")
@ApiOperation(value = "OTA进度条", notes = "前端轮询展示设备 OTA 任务进度;含 totalChunks/downloadedChunks 供可选分片文案;上传进度请用前端 multipart 的 on-progress")
public OtaProgressResultDTO otaProgress(@RequestParam(value = "sn", required = false) String sn,
@RequestParam(value = "version", required = false) String version,
HttpServletRequest request) {
OtaProgressResultDTO out = new OtaProgressResultDTO();
String snx = safeTrim(sn);
if (StringUtils.isEmpty(snx)) {
try {
snx = deviceTokenService.resolveSn(request);
} catch (Exception ignore) {
}
}
if (StringUtils.isEmpty(snx)) {
out.setCode(BboxApiConstants.CODE_PARAM_INVALID);
out.setSuccess("0");
out.setMsg("sn不能为空");
return out;
}
if (otaTasksService == null) {
out.setCode(BboxApiConstants.CODE_NOT_FOUND);
out.setSuccess("0");
out.setMsg("otaTasksService未启用");
out.setSn(snx);
return out;
}
String deviceId = resolveDeviceIdBySn(snx);
OtaTasks filter = new OtaTasks();
filter.setDeviceId(deviceId);
if (StringUtils.isNotEmpty(version)) {
filter.setFirmwareVersion(version);
}
List<OtaTasks> list = otaTasksService.selectOtaTasksList(filter);
List<OtaTasks> sorted = list == null ? new ArrayList<>() : new ArrayList<>(list);
sorted.sort(Comparator.comparing(this::resolveOtaSortTime, Comparator.nullsLast(Date::compareTo)).reversed());
OtaTasks latest = sorted.isEmpty() ? null : sorted.get(0);
out.setCode(BboxApiConstants.CODE_SUCCESS);
out.setSuccess("1");
out.setMsg(BboxApiConstants.MSG_SUCCESS);
out.setSn(snx);
out.setDeviceId(deviceId);
if (latest != null) {
out.setFirmwareVersion(latest.getFirmwareVersion());
out.setStatus(latest.getStatus());
out.setProgress(latest.getProgress() == null ? 0L : latest.getProgress());
out.setStartTime(latest.getStartTime());
out.setEndTime(latest.getEndTime());
out.setUpdateTime(latest.getUpdateTime());
} else {
out.setFirmwareVersion(version);
out.setStatus("NONE");
out.setProgress(0L);
}
attachOtaChunkProgressFields(out);
return out;
}
/**
* 为进度接口附加分片元数据totalChunks 与登录接口 count 同源downloadedChunks 由任务 progress 反推与分片下载时写回 DB 的规则一致
*/
private void attachOtaChunkProgressFields(OtaProgressResultDTO out) {
out.setChunkSize(otaChunkSize);
String fv = out.getFirmwareVersion();
if (StringUtils.isEmpty(fv)) {
return;
}
Integer total = computeOtaChunkCount(fv);
out.setTotalChunks(total);
if (total == null || total <= 0) {
return;
}
String st = out.getStatus();
if (isOtaFinishedSuccess(st)) {
out.setDownloadedChunks(total);
return;
}
long prog = out.getProgress() == null ? 0L : out.getProgress();
int est = (int) Math.round(prog * (double) total / 100.0);
est = Math.max(0, Math.min(est, total));
out.setDownloadedChunks(est);
}
private static boolean isOtaFinishedSuccess(String status) {
if (StringUtils.isEmpty(status)) {
return false;
}
String u = status.trim().toUpperCase(Locale.ROOT);
return "DONE".equals(u) || "SUCCESS".equals(u) || "COMPLETED".equals(u);
}
/**
* 构造 OTA 负载 binary32
* - 格式`[32字节SHA256原始摘要] + [固件]`
@ -554,7 +875,8 @@ public class IotAuthController {
String rel = base + version + ".bin";
Resource res = new ClassPathResource(rel);
if (!res.exists()) return null;
try (InputStream in = res.getInputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
try (InputStream in = res.getInputStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
byte[] buf = new byte[4096];
int r;
while ((r = in.read(buf)) != -1) bos.write(buf, 0, r);
@ -572,6 +894,27 @@ public class IotAuthController {
}
}
private String resolveDeviceIdBySn(String sn) {
String deviceId = sn;
if (bmsDevicesService != null) {
try {
BmsDevices dev = bmsDevicesService.selectBmsDevicesBySn(sn);
if (dev != null && StringUtils.isNotEmpty(dev.getDeviceId())) {
deviceId = dev.getDeviceId();
}
} catch (Exception ignore) {
}
}
return deviceId;
}
private Date resolveOtaSortTime(OtaTasks task) {
if (task == null) return null;
if (task.getUpdateTime() != null) return task.getUpdateTime();
if (task.getCreateTime() != null) return task.getCreateTime();
return task.getStartTime();
}
/**
* 计算 SHA256 的十六进制字符串
* - 输出小写长度 64 ASCII 字符串
@ -580,7 +923,7 @@ public class IotAuthController {
private static String sha256Hex(byte[] data) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] digest = md.digest(data);
StringBuilder sb = new StringBuilder(digest.length );
StringBuilder sb = new StringBuilder(digest.length);
for (byte b : digest) {
//转为 16 进制字符串
String s = Integer.toHexString(0xff & b);
@ -608,7 +951,9 @@ public class IotAuthController {
private static String fixLengthRight(String s, int len) {
if (s == null) s = "";
if (s.length() >= len) return s.substring(0, len);
if (s.length() >= len){
return s.substring(0, len);
}
StringBuilder sb = new StringBuilder(len);
sb.append(s);
while (sb.length() < len) sb.append('0');

View File

@ -3,6 +3,8 @@ package com.evobms.project.iot.controller;
import com.evobms.common.constant.BboxApiConstants;
import com.evobms.common.utils.StringUtils;
import com.evobms.project.iot.service.DeviceTokenService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
@ -25,6 +27,7 @@ import java.util.Map;
*/
@RestController
@RequestMapping("/iot/ota")
@Api(tags = "IoT OTA状态上报")
public class IotOtaController {
private static final Logger log = LoggerFactory.getLogger(IotOtaController.class);
@ -32,6 +35,7 @@ public class IotOtaController {
private DeviceTokenService deviceTokenService;
@PostMapping("/status")
@ApiOperation("上报OTA升级状态")
public Map<String, Object> reportOtaStatus(@RequestBody Map<String, String> body,
HttpServletRequest request) {
Map<String, Object> resp = new HashMap<>();
@ -48,7 +52,7 @@ public class IotOtaController {
MDC.put("sn", sn);
MDC.put("version", prevVersion);
// 规范化状态仅识别 "1" 为成功其余按失败处理仍返回成功表示上报接口调用成功
String normalizedStatus = "0";
if ("1".equals(statusStr)) {
normalizedStatus = "1";

View File

@ -0,0 +1,31 @@
package com.evobms.project.iot.domain.dto;
import com.evobms.project.ota.domain.OtaTasks;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@ApiModel(value = "OtaHistoryResultDTO", description = "OTA操作历史查询结果")
@Data
public class OtaHistoryResultDTO {
@ApiModelProperty(value = "状态码", example = "200")
private Integer code;
@ApiModelProperty(value = "是否成功1成功/0失败", example = "1")
private String success;
@ApiModelProperty(value = "提示信息", example = "success")
private String msg;
@ApiModelProperty(value = "设备SN", example = "137PBP1M11001AFCF0000007")
private String sn;
@ApiModelProperty(value = "设备ID平台侧deviceId", example = "BAT_001")
private String deviceId;
@ApiModelProperty(value = "任务列表(按最近时间倒序)")
private List<OtaTasks> taskList;
}

View File

@ -0,0 +1,15 @@
package com.evobms.project.iot.domain.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 设备登录DTO
*/
@ApiModel(value = "OtaLoginDto", description = "设备登录DTO")
@Data
public class OtaLoginDto {
@ApiModelProperty(value = "固件版本")
private String version;
}

View File

@ -0,0 +1,59 @@
package com.evobms.project.iot.domain.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@ApiModel(value = "OtaProgressResultDTO", description = "OTA进度查询结果用于进度条")
@Data
public class OtaProgressResultDTO {
@ApiModelProperty(value = "状态码", example = "200")
private Integer code;
@ApiModelProperty(value = "是否成功1成功/0失败", example = "1")
private String success;
@ApiModelProperty(value = "提示信息", example = "success")
private String msg;
@ApiModelProperty(value = "设备SN", example = "137PBP1M11001AFCF0000007")
private String sn;
@ApiModelProperty(value = "设备ID平台侧deviceId", example = "BAT_001")
private String deviceId;
@ApiModelProperty(value = "目标固件版本", example = "v1.0.3")
private String firmwareVersion;
@ApiModelProperty(value = "任务状态", example = "IN_PROGRESS")
private String status;
@ApiModelProperty(value = "进度(0-100)", example = "30")
private Long progress;
/** 与设备登录接口返回的 count、GET /iot/ota/meta 的 chunks 同一口径(按固件字节与 ota.chunk-size 计算) */
@ApiModelProperty(value = "OTA 分片总数(与设备侧 count 对齐;服务端根据仓库内固件计算,缺文件时为 null", example = "120")
private Integer totalChunks;
/**
* 基于当前任务的 progress totalChunks 推算的已拉取分片数 downloadOtaChunk 中按片更新 progress 的规则一致取整误差 ±1 片属正常
* 任务状态为 DONE 等终态成功时置为 totalChunks
*/
@ApiModelProperty(value = "推算的已下载分片数(来自 progress×totalChunks仅供展示无 totalChunks 时为 null", example = "54")
private Integer downloadedChunks;
@ApiModelProperty(value = "单分片字节数(与 ota.chunk-size 一致)", example = "1024")
private Integer chunkSize;
@ApiModelProperty(value = "开始时间")
private Date startTime;
@ApiModelProperty(value = "结束时间")
private Date endTime;
@ApiModelProperty(value = "更新时间")
private Date updateTime;
}

View File

@ -0,0 +1,18 @@
package com.evobms.project.iot.domain.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* OTA固件上传DTO
*/
@ApiModel(value = "OtaUploadDto", description = "OTA固件上传DTO")
@Data
public class OtaUploadDto {
@ApiModelProperty(value = "设备SN码")
private String deviceSn;
@ApiModelProperty(value = "固件版本")
private String version;
}

View File

@ -0,0 +1,24 @@
package com.evobms.project.iot.domain.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* OTA固件上传响应DTO
*/
@ApiModel(value = "OtaUploadResponseDto", description = "OTA固件上传响应DTO")
@Data
public class OtaUploadResponseDto {
@ApiModelProperty(value = "状态码")
private String code;
@ApiModelProperty(value = "是否成功")
private String success;
@ApiModelProperty(value = "上传进度")
private Integer progress;
@ApiModelProperty(value = "消息")
private String message;
}

View File

@ -0,0 +1,31 @@
package com.evobms.project.iot.domain.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@ApiModel(value = "OtaUploadResultDTO", description = "OTA固件上传结果")
@Data
public class OtaUploadResultDTO {
@ApiModelProperty(value = "状态码", example = "200")
private Integer code;
@ApiModelProperty(value = "是否成功1成功/0失败", example = "1")
private String success;
@ApiModelProperty(value = "提示信息", example = "success")
private String msg;
@ApiModelProperty(value = "固件落盘路径", example = "E:/OTA/v1.0.3.bin")
private String path;
@ApiModelProperty(value = "文件大小(字节)", example = "102400")
private Long size;
@ApiModelProperty(value = "分片总数", example = "100")
private Integer chunks;
@ApiModelProperty(value = "分片大小(字节)", example = "1024")
private Integer chunkSize;
}

View File

@ -1,6 +1,9 @@
package com.evobms.project.ota.controller;
import java.util.List;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
@ -12,14 +15,20 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.evobms.common.utils.StringUtils;
import com.evobms.framework.aspectj.lang.annotation.Log;
import com.evobms.framework.aspectj.lang.enums.BusinessType;
import com.evobms.project.ota.domain.OtaTasks;
import com.evobms.project.ota.dto.OtaTaskQueryDTO;
import com.evobms.project.ota.service.IOtaTasksService;
import com.evobms.project.system.domain.BmsDevices;
import com.evobms.project.system.service.IBmsDevicesService;
import com.evobms.framework.web.controller.BaseController;
import com.evobms.framework.web.domain.AjaxResult;
import com.evobms.common.utils.poi.ExcelUtil;
import com.evobms.framework.web.page.TableDataInfo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
/**
* OTA任务Controller
@ -29,16 +38,21 @@ import com.evobms.framework.web.page.TableDataInfo;
*/
@RestController
@RequestMapping("/ota/tasks")
@Api(tags = "OTA任务管理")
public class OtaTasksController extends BaseController
{
@Autowired
private IOtaTasksService otaTasksService;
@Autowired(required = false)
private IBmsDevicesService bmsDevicesService;
/**
* 查询OTA任务列表
*/
//@PreAuthorize("@ss.hasPermi('OTA:tasks:list')")
@GetMapping("/list")
@ApiOperation("查询OTA任务列表")
public TableDataInfo list(OtaTasks otaTasks)
{
startPage();
@ -46,12 +60,51 @@ public class OtaTasksController extends BaseController
return getDataTable(list);
}
/**
* 根据SN查询设备OTA更新信息
*/
@GetMapping("/sn/{sn}")
@ApiOperation(value = "根据SN查询OTA任务信息", notes = "返回设备对象、最新任务和任务列表,供前端展示设备升级信息")
public AjaxResult getBySn(@PathVariable("sn") String sn)
{
if (StringUtils.isEmpty(sn))
{
return AjaxResult.error("sn不能为空");
}
BmsDevices device = null;
String deviceId = sn;
if (bmsDevicesService != null)
{
device = bmsDevicesService.selectBmsDevicesBySn(sn);
if (device != null && StringUtils.isNotEmpty(device.getDeviceId()))
{
deviceId = device.getDeviceId();
}
}
OtaTasks query = new OtaTasks();
query.setDeviceId(deviceId);
List<OtaTasks> taskList = otaTasksService.selectOtaTasksList(query);
List<OtaTasks> sortedTaskList = taskList == null ? new ArrayList<>() : new ArrayList<>(taskList);
sortedTaskList.sort(Comparator.comparing(this::resolveSortTime,
Comparator.nullsLast(Date::compareTo)).reversed());
OtaTaskQueryDTO dto = new OtaTaskQueryDTO();
dto.setSn(sn);
dto.setDevice(device);
dto.setTaskList(sortedTaskList);
dto.setLatestTask(sortedTaskList.isEmpty() ? null : sortedTaskList.get(0));
return success(dto);
}
/**
* 导出OTA任务列表
*/
//@PreAuthorize("@ss.hasPermi('OTA:tasks:export')")
@Log(title = "OTA任务", businessType = BusinessType.EXPORT)
@PostMapping("/export")
@ApiOperation("导出OTA任务列表")
public void export(HttpServletResponse response, OtaTasks otaTasks)
{
List<OtaTasks> list = otaTasksService.selectOtaTasksList(otaTasks);
@ -64,6 +117,7 @@ public class OtaTasksController extends BaseController
*/
//@PreAuthorize("@ss.hasPermi('OTA:tasks:query')")
@GetMapping(value = "/{id}")
@ApiOperation("获取OTA任务详细信息")
public AjaxResult getInfo(@PathVariable("id") String id)
{
return success(otaTasksService.selectOtaTasksById(id));
@ -75,6 +129,7 @@ public class OtaTasksController extends BaseController
//@PreAuthorize("@ss.hasPermi('OTA:tasks:add')")
@Log(title = "OTA任务", businessType = BusinessType.INSERT)
@PostMapping
@ApiOperation("新增OTA任务")
public AjaxResult add(@RequestBody OtaTasks otaTasks)
{
return toAjax(otaTasksService.insertOtaTasks(otaTasks));
@ -86,6 +141,7 @@ public class OtaTasksController extends BaseController
//@PreAuthorize("@ss.hasPermi('OTA:tasks:edit')")
@Log(title = "OTA任务", businessType = BusinessType.UPDATE)
@PutMapping
@ApiOperation("修改OTA任务")
public AjaxResult edit(@RequestBody OtaTasks otaTasks)
{
return toAjax(otaTasksService.updateOtaTasks(otaTasks));
@ -96,8 +152,26 @@ public class OtaTasksController extends BaseController
*/
@Log(title = "OTA任务", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
@ApiOperation("删除OTA任务")
public AjaxResult remove(@PathVariable String[] ids)
{
return toAjax(otaTasksService.deleteOtaTasksByIds(ids));
}
private Date resolveSortTime(OtaTasks task)
{
if (task == null)
{
return null;
}
if (task.getUpdateTime() != null)
{
return task.getUpdateTime();
}
if (task.getCreateTime() != null)
{
return task.getCreateTime();
}
return task.getStartTime();
}
}

View File

@ -1,7 +1,11 @@
package com.evobms.project.ota.domain;
import java.util.Date;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.evobms.framework.aspectj.lang.annotation.Excel;
@ -9,157 +13,62 @@ import com.evobms.framework.web.domain.BaseEntity;
/**
* OTA任务对象 ota_tasks
*
*
* @author ruoyi
* @date 2025-10-14
*/
@EqualsAndHashCode(callSuper = true)
@ApiModel(description = "OTA任务对象")
@Data
public class OtaTasks extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 任务IDUUID */
@ApiModelProperty(value = "任务IDUUID")
private String id;
/** 设备ID */
@ApiModelProperty(value = "设备ID")
@Excel(name = "设备ID")
private String deviceId;
/** 任务名称 */
@ApiModelProperty(value = "任务名称")
@Excel(name = "任务名称")
private String taskName;
/** 目标固件版本 */
@ApiModelProperty(value = "目标固件版本")
@Excel(name = "目标固件版本")
private String firmwareVersion;
/** 任务状态 */
@ApiModelProperty(value = "任务状态")
@Excel(name = "任务状态")
private String status;
/** 升级进度(0-100) */
@ApiModelProperty(value = "升级进度(0-100)")
@Excel(name = "升级进度(0-100)")
private Long progress;
/** 开始时间 */
@ApiModelProperty(value = "开始时间")
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "开始时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date startTime;
/** 结束时间 */
@ApiModelProperty(value = "结束时间")
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "结束时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date endTime;
/** 错误信息 */
@ApiModelProperty(value = "错误信息")
@Excel(name = "错误信息")
private String errorMessage;
public void setId(String id)
{
this.id = id;
}
public String getId()
{
return id;
}
public void setDeviceId(String deviceId)
{
this.deviceId = deviceId;
}
public String getDeviceId()
{
return deviceId;
}
public void setTaskName(String taskName)
{
this.taskName = taskName;
}
public String getTaskName()
{
return taskName;
}
public void setFirmwareVersion(String firmwareVersion)
{
this.firmwareVersion = firmwareVersion;
}
public String getFirmwareVersion()
{
return firmwareVersion;
}
public void setStatus(String status)
{
this.status = status;
}
public String getStatus()
{
return status;
}
public void setProgress(Long progress)
{
this.progress = progress;
}
public Long getProgress()
{
return progress;
}
public void setStartTime(Date startTime)
{
this.startTime = startTime;
}
public Date getStartTime()
{
return startTime;
}
public void setEndTime(Date endTime)
{
this.endTime = endTime;
}
public Date getEndTime()
{
return endTime;
}
public void setErrorMessage(String errorMessage)
{
this.errorMessage = errorMessage;
}
public String getErrorMessage()
{
return errorMessage;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("deviceId", getDeviceId())
.append("taskName", getTaskName())
.append("firmwareVersion", getFirmwareVersion())
.append("status", getStatus())
.append("progress", getProgress())
.append("startTime", getStartTime())
.append("endTime", getEndTime())
.append("errorMessage", getErrorMessage())
.append("createTime", getCreateTime())
.append("updateTime", getUpdateTime())
.append("updateBy", getUpdateBy())
.append("createBy", getCreateBy())
.toString();
}
}

View File

@ -0,0 +1,26 @@
package com.evobms.project.ota.dto;
import com.evobms.project.ota.domain.OtaTasks;
import com.evobms.project.system.domain.BmsDevices;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
@ApiModel(value = "OtaTaskQueryDTO", description = "根据SN查询设备OTA任务响应")
public class OtaTaskQueryDTO {
@ApiModelProperty(value = "设备SN", example = "137PBP1M11001AFCF0000007")
private String sn;
@ApiModelProperty(value = "设备对象")
private BmsDevices device;
@ApiModelProperty(value = "最新一条OTA任务")
private OtaTasks latestTask;
@ApiModelProperty(value = "该设备的OTA任务列表按最近更新时间倒序")
private List<OtaTasks> taskList;
}

View File

@ -0,0 +1,113 @@
package com.evobms.project.summary.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.evobms.framework.aspectj.lang.annotation.Log;
import com.evobms.framework.aspectj.lang.enums.BusinessType;
import com.evobms.project.summary.domain.ChargeDischargeSummary;
import com.evobms.project.summary.service.IChargeDischargeSummaryService;
import com.evobms.framework.web.controller.BaseController;
import com.evobms.framework.web.domain.AjaxResult;
import com.evobms.common.utils.poi.ExcelUtil;
import com.evobms.framework.web.page.TableDataInfo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
/**
* 累计充放电量Controller
*
* @author 田志阳
* @date 2026-01-09
*/
@RestController
@RequestMapping("/summary/summary")
@Api(tags = "充放电统计")
public class ChargeDischargeSummaryController extends BaseController
{
@Autowired
private IChargeDischargeSummaryService chargeDischargeSummaryService;
/**
* 查询累计充放电量列表
*/
@PreAuthorize("@ss.hasPermi('summary:summary:list')")
@GetMapping("/list")
@ApiOperation("查询累计充放电量列表")
public TableDataInfo list(ChargeDischargeSummary chargeDischargeSummary)
{
startPage();
List<ChargeDischargeSummary> list = chargeDischargeSummaryService.selectChargeDischargeSummaryList(chargeDischargeSummary);
return getDataTable(list);
}
/**
* 导出累计充放电量列表
*/
@PreAuthorize("@ss.hasPermi('summary:summary:export')")
@Log(title = "累计充放电量", businessType = BusinessType.EXPORT)
@PostMapping("/export")
@ApiOperation("导出累计充放电量列表")
public void export(HttpServletResponse response, ChargeDischargeSummary chargeDischargeSummary)
{
List<ChargeDischargeSummary> list = chargeDischargeSummaryService.selectChargeDischargeSummaryList(chargeDischargeSummary);
ExcelUtil<ChargeDischargeSummary> util = new ExcelUtil<ChargeDischargeSummary>(ChargeDischargeSummary.class);
util.exportExcel(response, list, "累计充放电量数据");
}
/**
* 获取累计充放电量详细信息
*/
@PreAuthorize("@ss.hasPermi('summary:summary:query')")
@GetMapping(value = "/{id}")
@ApiOperation("获取累计充放电量详细信息")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(chargeDischargeSummaryService.selectChargeDischargeSummaryById(id));
}
/**
* 新增累计充放电量
*/
@PreAuthorize("@ss.hasPermi('summary:summary:add')")
@Log(title = "累计充放电量", businessType = BusinessType.INSERT)
@PostMapping
@ApiOperation("新增累计充放电量")
public AjaxResult add(@RequestBody ChargeDischargeSummary chargeDischargeSummary)
{
return toAjax(chargeDischargeSummaryService.insertChargeDischargeSummary(chargeDischargeSummary));
}
/**
* 修改累计充放电量
*/
@PreAuthorize("@ss.hasPermi('summary:summary:edit')")
@Log(title = "累计充放电量", businessType = BusinessType.UPDATE)
@PutMapping
@ApiOperation("修改累计充放电量")
public AjaxResult edit(@RequestBody ChargeDischargeSummary chargeDischargeSummary)
{
return toAjax(chargeDischargeSummaryService.updateChargeDischargeSummary(chargeDischargeSummary));
}
/**
* 删除累计充放电量
*/
@PreAuthorize("@ss.hasPermi('summary:summary:remove')")
@Log(title = "累计充放电量", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
@ApiOperation("删除累计充放电量")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(chargeDischargeSummaryService.deleteChargeDischargeSummaryByIds(ids));
}
}

View File

@ -0,0 +1,52 @@
package com.evobms.project.summary.domain;
import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.evobms.framework.aspectj.lang.annotation.Excel;
import com.evobms.framework.web.domain.BaseEntity;
/**
* 累计充放电量对象 charge_discharge_summary
*
* @author 田志阳
* @date 2026-01-09
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "ChargeDischargeSummary", description = "累计充放电量对象")
public class ChargeDischargeSummary extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键ID */
private Long id;
/** 设备ID */
@Excel(name = "设备ID")
@ApiModelProperty(value = "设备ID")
private String deviceId;
/** 时间戳 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss ")
@ApiModelProperty(value = "时间戳")
@Excel(name = "时间戳", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date timestamp;
/** 累计放电(kWh) */
@ApiModelProperty(value = "累计放电(kWh)")
@Excel(name = "累计放电(kWh)")
private BigDecimal dischargeKwh;
/** 累计充电kWh) */
@ApiModelProperty(value = "累计充电kWh")
@Excel(name = "累计充电(kWh)")
private BigDecimal chargeKwh;
}

View File

@ -0,0 +1,62 @@
package com.evobms.project.summary.mapper;
import java.util.List;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.evobms.project.summary.domain.ChargeDischargeSummary;
/**
* 累计充放电量Mapper接口
*
* @author 田志阳
* @date 2026-01-09
*/
public interface ChargeDischargeSummaryMapper extends BaseMapper<ChargeDischargeSummary> {
/**
* 查询累计充放电量
*
* @param id 累计充放电量主键
* @return 累计充放电量
*/
public ChargeDischargeSummary selectChargeDischargeSummaryById(Long id);
/**
* 查询累计充放电量列表
*
* @param chargeDischargeSummary 累计充放电量
* @return 累计充放电量集合
*/
public List<ChargeDischargeSummary> selectChargeDischargeSummaryList(ChargeDischargeSummary chargeDischargeSummary);
/**
* 新增累计充放电量
*
* @param chargeDischargeSummary 累计充放电量
* @return 结果
*/
public int insertChargeDischargeSummary(ChargeDischargeSummary chargeDischargeSummary);
/**
* 修改累计充放电量
*
* @param chargeDischargeSummary 累计充放电量
* @return 结果
*/
public int updateChargeDischargeSummary(ChargeDischargeSummary chargeDischargeSummary);
/**
* 删除累计充放电量
*
* @param id 累计充放电量主键
* @return 结果
*/
public int deleteChargeDischargeSummaryById(Long id);
/**
* 批量删除累计充放电量
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteChargeDischargeSummaryByIds(Long[] ids);
}

View File

@ -0,0 +1,61 @@
package com.evobms.project.summary.service;
import java.util.List;
import com.evobms.project.summary.domain.ChargeDischargeSummary;
/**
* 累计充放电量Service接口
*
* @author 田志阳
* @date 2026-01-09
*/
public interface IChargeDischargeSummaryService
{
/**
* 查询累计充放电量
*
* @param id 累计充放电量主键
* @return 累计充放电量
*/
public ChargeDischargeSummary selectChargeDischargeSummaryById(Long id);
/**
* 查询累计充放电量列表
*
* @param chargeDischargeSummary 累计充放电量
* @return 累计充放电量集合
*/
public List<ChargeDischargeSummary> selectChargeDischargeSummaryList(ChargeDischargeSummary chargeDischargeSummary);
/**
* 新增累计充放电量
*
* @param chargeDischargeSummary 累计充放电量
* @return 结果
*/
public int insertChargeDischargeSummary(ChargeDischargeSummary chargeDischargeSummary);
/**
* 修改累计充放电量
*
* @param chargeDischargeSummary 累计充放电量
* @return 结果
*/
public int updateChargeDischargeSummary(ChargeDischargeSummary chargeDischargeSummary);
/**
* 批量删除累计充放电量
*
* @param ids 需要删除的累计充放电量主键集合
* @return 结果
*/
public int deleteChargeDischargeSummaryByIds(Long[] ids);
/**
* 删除累计充放电量信息
*
* @param id 累计充放电量主键
* @return 结果
*/
public int deleteChargeDischargeSummaryById(Long id);
}

View File

@ -0,0 +1,102 @@
package com.evobms.project.summary.service.impl;
import java.util.List;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evobms.common.utils.DateUtils;
import com.evobms.project.vehicledata.domain.VehicleData;
import com.evobms.project.vehicledata.mapper.VehicleDataMapper;
import com.evobms.project.vehicledata.service.IVehicleDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.evobms.project.summary.mapper.ChargeDischargeSummaryMapper;
import com.evobms.project.summary.domain.ChargeDischargeSummary;
import com.evobms.project.summary.service.IChargeDischargeSummaryService;
/**
* 累计充放电量Service业务层处理
*
* @author 田志阳
* @date 2026-01-09
*/
@Service
public class ChargeDischargeSummaryServiceImpl extends ServiceImpl<ChargeDischargeSummaryMapper, ChargeDischargeSummary> implements IChargeDischargeSummaryService
{
@Autowired
private ChargeDischargeSummaryMapper chargeDischargeSummaryMapper;
/**
* 查询累计充放电量
*
* @param id 累计充放电量主键
* @return 累计充放电量
*/
@Override
public ChargeDischargeSummary selectChargeDischargeSummaryById(Long id)
{
return chargeDischargeSummaryMapper.selectChargeDischargeSummaryById(id);
}
/**
* 查询累计充放电量列表
*
* @param chargeDischargeSummary 累计充放电量
* @return 累计充放电量
*/
@Override
public List<ChargeDischargeSummary> selectChargeDischargeSummaryList(ChargeDischargeSummary chargeDischargeSummary)
{
return chargeDischargeSummaryMapper.selectChargeDischargeSummaryList(chargeDischargeSummary);
}
/**
* 新增累计充放电量
*
* @param chargeDischargeSummary 累计充放电量
* @return 结果
*/
@Override
public int insertChargeDischargeSummary(ChargeDischargeSummary chargeDischargeSummary)
{
chargeDischargeSummary.setCreateTime(DateUtils.getNowDate());
return chargeDischargeSummaryMapper.insertChargeDischargeSummary(chargeDischargeSummary);
}
/**
* 修改累计充放电量
*
* @param chargeDischargeSummary 累计充放电量
* @return 结果
*/
@Override
public int updateChargeDischargeSummary(ChargeDischargeSummary chargeDischargeSummary)
{
chargeDischargeSummary.setUpdateTime(DateUtils.getNowDate());
return chargeDischargeSummaryMapper.updateChargeDischargeSummary(chargeDischargeSummary);
}
/**
* 批量删除累计充放电量
*
* @param ids 需要删除的累计充放电量主键
* @return 结果
*/
@Override
public int deleteChargeDischargeSummaryByIds(Long[] ids)
{
return chargeDischargeSummaryMapper.deleteChargeDischargeSummaryByIds(ids);
}
/**
* 删除累计充放电量信息
*
* @param id 累计充放电量主键
* @return 结果
*/
@Override
public int deleteChargeDischargeSummaryById(Long id)
{
return chargeDischargeSummaryMapper.deleteChargeDischargeSummaryById(id);
}
}

View File

@ -1,43 +1,89 @@
package com.evobms.project.system.controller;
import java.util.List;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import javax.servlet.http.HttpServletResponse;
import com.evobms.project.vehicledata.domain.VehicleLocation;
import com.evobms.project.vehicledata.service.IVehicleLocationService;
import io.swagger.annotations.ApiParam;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import com.evobms.framework.aspectj.lang.annotation.Log;
import com.evobms.framework.aspectj.lang.enums.BusinessType;
import com.evobms.project.system.domain.BmsDevices;
import com.evobms.project.system.service.IBmsDevicesService;
import com.evobms.framework.web.controller.BaseController;
import com.evobms.framework.web.domain.AjaxResult;
import com.evobms.framework.web.domain.R;
import com.evobms.common.constant.HttpStatus;
import com.evobms.common.utils.poi.ExcelUtil;
import com.evobms.framework.web.page.TableDataInfo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.evobms.project.vehicledata.dto.DeviceHistoryFlatRowDTO;
import com.evobms.project.vehicledata.dto.DeviceRealtimeDTO;
import com.evobms.project.vehicledata.dto.DeviceRealtimeAggregateDTO;
import com.evobms.project.vehicledata.dto.NameValueDTO;
import com.evobms.project.vehicledata.dto.DeviceRealtimeDTO.SystemStatusSection;
import com.evobms.project.vehicledata.dto.DeviceRealtimeDTO.RunStatusSection;
import com.evobms.project.vehicledata.dto.DeviceRealtimeDTO.ChargeSection;
import com.evobms.project.vehicledata.dto.DeviceRealtimeDTO.StatisticsSection;
import com.evobms.project.vehicledata.dto.LineSeriesDTO;
import com.evobms.project.vehicledata.dto.TimePointDTO;
import com.evobms.project.vehicledata.dto.ChargeDischargeMonthDTO;
import com.evobms.project.vehicledata.dto.ChargeDischargeRecordDTO;
import com.evobms.project.vehicledata.dto.ChargeDischargeHistoryDTO;
import com.evobms.project.vehicledata.service.DeviceRealtimeHistoryAssembler;
import com.evobms.project.vehicledata.service.IVehicleDataService;
import com.evobms.project.vehicledata.domain.VehicleData;
import com.evobms.project.battery.service.IExtremeValuesService;
import com.evobms.project.battery.service.ISubsystemVoltageService;
import com.evobms.project.battery.service.ISubsystemTemperatureService;
import com.evobms.project.battery.domain.ExtremeValues;
import com.evobms.project.battery.domain.SubsystemVoltage;
import com.evobms.project.battery.domain.SubsystemTemperature;
import com.evobms.project.bms.service.*;
import com.evobms.project.bms.domain.*;
/**
* BBOX管理Controller
*
* @author 田志阳
* @date 2025-10-10
* @date 2025-11-15
*/
@RestController
@RequestMapping("/devices/devices")
@Api(tags = "BBOX设备管理")
public class BmsDevicesController extends BaseController
{
@Autowired
private IBmsDevicesService bmsDevicesService;
@Autowired private IVehicleDataService vehicleDataService;
@Autowired private IExtremeValuesService extremeValuesService;
@Autowired private IBmsSystemStatusService bmsSystemStatusService;
@Autowired private IBmsChargeDetailService bmsChargeDetailService;
@Autowired private IBmsChargeTemperatureService bmsChargeTemperatureService;
@Autowired private IBmsVoltageChannelDataService bmsVoltageChannelDataService;
@Autowired private IBmsSopDataService bmsSopDataService;
@Autowired private IVehicleLocationService vehicleLocationService;
@Autowired private IBmsRuntimeDataService bmsRuntimeDataService;
@Autowired private ISubsystemVoltageService subsystemVoltageService;
@Autowired private ISubsystemTemperatureService subsystemTemperatureService;
@Autowired private DeviceRealtimeHistoryAssembler deviceRealtimeHistoryAssembler;
/**
* 查询BBOX管理列表
*/
@PreAuthorize("@ss.hasPermi('system:devices:list')")
@GetMapping("/list")
@ApiOperation("查询BBOX设备列表")
public TableDataInfo list(BmsDevices bmsDevices)
{
startPage();
@ -45,11 +91,131 @@ public class BmsDevicesController extends BaseController
return getDataTable(list);
}
@GetMapping("/history/{deviceId}")
@ApiOperation(value = "运行历史计算器(充/放电统计)", response = ChargeDischargeHistoryDTO.class)
public R<ChargeDischargeHistoryDTO> history(
@ApiParam(value = "设备ID", required = true) @PathVariable("deviceId") String deviceId,
@ApiParam(value = "开始时间戳(毫秒)", required = false) @RequestParam(value = "start", required = false) Long startMillis,
@ApiParam(value = "结束时间戳(毫秒)", required = false) @RequestParam(value = "end", required = false) Long endMillis
) {
Date end = endMillis == null ? new Date() : new Date(endMillis);
Date start = startMillis == null ? new Date(end.getTime() - 30L * 86400_000L) : new Date(startMillis);
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat mf = new SimpleDateFormat("yyyy-MM");
VehicleData q = new VehicleData();
Map<String, Object> params = new HashMap<>();
params.put("beginTime", df.format(start));
params.put("endTime", df.format(end));
q.setDeviceId(deviceId);
q.setParams(params);
List<VehicleData> all = vehicleDataService.selectVehicleDataList(q);
all.removeIf(v -> v.getTimestamp() == null);
all.sort(Comparator.comparing(VehicleData::getTimestamp));
List<ChargeDischargeRecordDTO> records = new ArrayList<>();
String currentStatus = null;
int segmentStartIdx = -1;
Integer prevSoc = null;
for (int i = 0; i < all.size(); i++) {
VehicleData v = all.get(i);
Integer soc = v.getSoc();
BigDecimal cur = v.getTotalCurrent();
String status = null;
if (cur != null) {
int cmp = cur.compareTo(BigDecimal.ZERO);
if (cmp > 0) status = "充电";
else if (cmp < 0) status = "放电";
}
if (status == null && prevSoc != null && soc != null) {
if (soc > prevSoc) status = "充电";
else if (soc < prevSoc) status = "放电";
}
if (status == null && v.getChargingStatus() != null) {
Integer st = v.getChargingStatus();
if (st == 1 || st == 2) status = "充电";
else if (st == 3) status = "放电";
}
if (status == null) {
prevSoc = soc;
continue;
}
if (currentStatus == null) {
currentStatus = status;
segmentStartIdx = i;
} else if (!currentStatus.equals(status)) {
// finalize previous segment
ChargeDischargeRecordDTO rec = new ChargeDischargeRecordDTO();
rec.setStatus(currentStatus);
com.evobms.project.vehicledata.domain.VehicleData sv = all.get(segmentStartIdx);
com.evobms.project.vehicledata.domain.VehicleData evd = all.get(i - 1);
rec.setStartTime(df.format(sv.getTimestamp()));
rec.setEndTime(df.format(evd.getTimestamp()));
rec.setStartSoc(sv.getSoc());
rec.setEndSoc(evd.getSoc());
long seconds = (evd.getTimestamp().getTime() - sv.getTimestamp().getTime()) / 1000L;
java.math.BigDecimal hours = new java.math.BigDecimal(seconds).divide(new java.math.BigDecimal(3600), 2, java.math.RoundingMode.HALF_UP);
rec.setDurationHours(hours);
// 忽略过短区段小于5分钟
if (seconds >= 300) {
records.add(rec);
}
currentStatus = status;
segmentStartIdx = i;
}
prevSoc = soc;
}
// finalize last
if (currentStatus != null && segmentStartIdx >= 0 && all.size() - 1 >= segmentStartIdx) {
ChargeDischargeRecordDTO rec = new ChargeDischargeRecordDTO();
rec.setStatus(currentStatus);
com.evobms.project.vehicledata.domain.VehicleData sv = all.get(segmentStartIdx);
com.evobms.project.vehicledata.domain.VehicleData evd = all.get(all.size() - 1);
rec.setStartTime(df.format(sv.getTimestamp()));
rec.setEndTime(df.format(evd.getTimestamp()));
rec.setStartSoc(sv.getSoc());
rec.setEndSoc(evd.getSoc());
long seconds = (evd.getTimestamp().getTime() - sv.getTimestamp().getTime()) / 1000L;
java.math.BigDecimal hours = new java.math.BigDecimal(seconds).divide(new java.math.BigDecimal(3600), 2, java.math.RoundingMode.HALF_UP);
rec.setDurationHours(hours);
if (seconds >= 300) {
records.add(rec);
}
}
java.util.Map<String, java.math.BigDecimal[]> monthAgg = new java.util.HashMap<>();
for (ChargeDischargeRecordDTO rec : records) {
String m = rec.getStartTime().substring(0, 7);
java.math.BigDecimal[] pair = monthAgg.computeIfAbsent(m, k -> new java.math.BigDecimal[]{java.math.BigDecimal.ZERO, java.math.BigDecimal.ZERO});
if ("充电".equals(rec.getStatus())) {
pair[0] = pair[0].add(rec.getDurationHours());
} else {
pair[1] = pair[1].add(rec.getDurationHours());
}
}
java.util.List<ChargeDischargeMonthDTO> months = new java.util.ArrayList<>();
java.util.List<String> keys = new java.util.ArrayList<>(monthAgg.keySet());
keys.sort(String::compareTo);
for (String k : keys) {
ChargeDischargeMonthDTO m = new ChargeDischargeMonthDTO();
m.setMonth(k);
java.math.BigDecimal[] pair = monthAgg.get(k);
m.setChargeHours(pair[0]);
m.setDischargeHours(pair[1]);
months.add(m);
}
ChargeDischargeHistoryDTO out = new ChargeDischargeHistoryDTO();
out.setMonths(months);
out.setRecords(records);
return R.ok(out);
}
/**
* 导出BBOX管理列表
*/
@PreAuthorize("@ss.hasPermi('system:devices:export')")
@Log(title = "BBOX管理", businessType = BusinessType.EXPORT)
@PostMapping("/export")
@ApiOperation("导出BBOX设备列表")
public void export(HttpServletResponse response, BmsDevices bmsDevices)
{
List<BmsDevices> list = bmsDevicesService.selectBmsDevicesList(bmsDevices);
@ -60,17 +226,135 @@ public class BmsDevicesController extends BaseController
/**
* 获取BBOX管理详细信息
*/
@PreAuthorize("@ss.hasPermi('system:devices:query')")
@GetMapping(value = "/{id}")
@ApiOperation("获取BBOX设备详细信息")
public AjaxResult getInfo(@PathVariable("id") String id)
{
return success(bmsDevicesService.selectBmsDevicesById(id));
}
/** 子系统去重预取行数(按 timestamp 倒序,一般远小于子系统数×上报次数) */
private static final int REALTIME_SUBSYSTEM_PREFETCH = 32;
/** 24h 趋势曲线最多点数 */
private static final int REALTIME_TREND_MAX_POINTS = 360;
@GetMapping("/realtime/{deviceId}")
@ApiOperation(value = "设备实时数据", notes = "默认不含 dashboard 趋势(24h),避免超时;趋势请调 includeTrends=true 或 GET .../trends", response = DeviceRealtimeAggregateDTO.class)
public R<DeviceRealtimeAggregateDTO> realtime(@PathVariable("deviceId") String deviceId, @ApiParam("是否包含 dashboard 24h 趋势(较慢,易触发前端 10s 超时)") @RequestParam(value = "includeTrends", required = false, defaultValue = "true") boolean includeTrends) {
RealtimeSnapshot snap = loadRealtimeSnapshot(deviceId);
DeviceRealtimeAggregateDTO agg = new DeviceRealtimeAggregateDTO();
agg.setDeviceId(deviceId);
fillAggregateFromSnapshot(agg, snap);
if (includeTrends) {
SimpleDateFormat tf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date now = new Date();
Date trendBegin = new Date(now.getTime() - 24L * 3600_000L);
agg.setDashboard(buildRealtimeDashboard(deviceId, snap.vehicleData, snap.extremeValues, snap.systemStatus, snap.chargeDetail, snap.chargeTemperatures, snap.voltageChannels, trendBegin, now, tf));
}
return R.ok(agg);
}
@GetMapping("/realtime/{deviceId}/trends")
@ApiOperation(value = "设备实时 24h 趋势(dashboard)", notes = "与 realtime 拆分,避免与快照同请求导致 Axios 10s 超时", response = DeviceRealtimeDTO.class)
public R<DeviceRealtimeDTO> realtimeTrends(
@PathVariable("deviceId") String deviceId,
@ApiParam("向前小时数,默认 24") @RequestParam(value = "hours", required = false, defaultValue = "24") int hours) {
RealtimeSnapshot snap = loadRealtimeSnapshot(deviceId);
SimpleDateFormat tf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date now = new Date();
int h = hours <= 0 ? 24 : Math.min(hours, 168);
Date trendBegin = new Date(now.getTime() - (long) h * 3600_000L);
return R.ok(buildRealtimeDashboard(deviceId, snap.vehicleData, snap.extremeValues, snap.systemStatus,
snap.chargeDetail, snap.chargeTemperatures, snap.voltageChannels, trendBegin, now, tf));
}
/**
* {@link #realtime(String)} 同源字段的历史平铺列表时间范围内全部 vehicle_data 在库内分页pageNum/pageSize
* 每页再与其它表按时间点合并pageSize 仅支持 1020100500
*/
@PreAuthorize("@ss.hasPermi('system:devices:query')")
@GetMapping("/realtime/history/{deviceId}")
@ApiOperation(value = "设备实时同源历史(平铺分页)", notes = "对 beginTime~endTime 内全部整车记录分页;参数 pageNum、pageSize(10|20|100|500)total 为符合条件的总条数", response = DeviceHistoryFlatRowDTO.class)
public TableDataInfo realtimeHistoryFlat(
@ApiParam(value = "设备ID", required = true) @PathVariable("deviceId") String deviceId,
@ApiParam(value = "开始时间戳(毫秒),与 beginTime 二选一") @RequestParam(value = "start", required = false) Long startMillis,
@ApiParam(value = "结束时间戳(毫秒),与 endTime 二选一") @RequestParam(value = "end", required = false) Long endMillis,
@ApiParam(value = "开始时间 yyyy-MM-dd HH:mm:ss") @RequestParam(value = "beginTime", required = false) String beginTimeStr,
@ApiParam(value = "结束时间 yyyy-MM-dd HH:mm:ss") @RequestParam(value = "endTime", required = false) String endTimeStr,
@ApiParam(value = "排序desc 默认最新在前asc 时间正序") @RequestParam(value = "order", required = false, defaultValue = "desc") String order,
@ApiParam(value = "是否查询总条数 COUNT大数据量可传 false 加快首屏") @RequestParam(value = "count", required = false, defaultValue = "true") boolean countTotal
) {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date end;
Date start;
boolean userRange = startMillis != null || (beginTimeStr != null && !beginTimeStr.isEmpty());
try {
end = parseHistoryEnd(endMillis, endTimeStr, df);
start = parseHistoryStart(startMillis, beginTimeStr, df, end);
} catch (ParseException e) {
TableDataInfo err = new TableDataInfo();
err.setCode(HttpStatus.ERROR);
err.setMsg("时间解析失败,请使用 yyyy-MM-dd HH:mm:ss 或毫秒时间戳");
err.setRows(Collections.emptyList());
err.setTotal(0);
return err;
}
boolean desc = !"asc".equalsIgnoreCase(order);
if (!userRange) {
start = clampHistoryStart(start, end);
}
int pageSize = com.evobms.common.utils.PageUtils.startHistoryPage(countTotal);
List<VehicleData> vehicles = vehicleDataService.selectVehicleDataListForHistory(deviceId, start, end, desc);
// total 必须来自 PageHelper 分页查询结果不能用组装后的 rows否则 total=当前页条数
long total = new com.github.pagehelper.PageInfo<>(vehicles).getTotal();
SimpleDateFormat tf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
List<DeviceHistoryFlatRowDTO> rows = deviceRealtimeHistoryAssembler.build(deviceId, vehicles, tf, pageSize);
TableDataInfo table = getDataTable(rows);
table.setTotal(total);
return table;
}
private static Date parseHistoryEnd(Long endMillis, String endTimeStr, SimpleDateFormat df) throws ParseException {
if (endMillis != null) {
return new Date(endMillis);
}
if (endTimeStr != null && !endTimeStr.isEmpty()) {
return df.parse(endTimeStr);
}
return new Date();
}
private static Date parseHistoryStart(Long startMillis, String beginTimeStr, SimpleDateFormat df, Date end) throws ParseException {
if (startMillis != null) {
return new Date(startMillis);
}
if (beginTimeStr != null && !beginTimeStr.isEmpty()) {
return df.parse(beginTimeStr);
}
return new Date(end.getTime() - 7L * 86400_000L);
}
/** 单次查询时间窗上限 7 天,避免一次扫过多行 */
private static Date clampHistoryStart(Date start, Date end) {
if (start == null || end == null) {
return start;
}
long maxWindow = 7L * 86400_000L;
if (end.getTime() - start.getTime() > maxWindow) {
return new Date(end.getTime() - maxWindow);
}
return start;
}
/**
* 新增BBOX管理
*/
@PreAuthorize("@ss.hasPermi('system:devices:add')")
@Log(title = "BBOX管理", businessType = BusinessType.INSERT)
@PostMapping
@ApiOperation("新增BBOX设备")
public AjaxResult add(@RequestBody BmsDevices bmsDevices)
{
return toAjax(bmsDevicesService.insertBmsDevices(bmsDevices));
@ -79,8 +363,10 @@ public class BmsDevicesController extends BaseController
/**
* 修改BBOX管理
*/
@PreAuthorize("@ss.hasPermi('system:devices:edit')")
@Log(title = "BBOX管理", businessType = BusinessType.UPDATE)
@PutMapping
@ApiOperation("修改BBOX设备")
public AjaxResult edit(@RequestBody BmsDevices bmsDevices)
{
return toAjax(bmsDevicesService.updateBmsDevices(bmsDevices));
@ -89,10 +375,400 @@ public class BmsDevicesController extends BaseController
/**
* 删除BBOX管理
*/
@PreAuthorize("@ss.hasPermi('system:devices:remove')")
@Log(title = "BBOX管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
@DeleteMapping("/{ids}")
@ApiOperation("删除BBOX设备")
public AjaxResult remove(@PathVariable String[] ids)
{
return toAjax(bmsDevicesService.deleteBmsDevicesByIds(ids));
}
/** 扁平视图:与原始列表共用同一批查询结果,避免重复组装且趋势只在 dashboard 中暴露一份 */
private DeviceRealtimeDTO buildRealtimeDashboard(
String deviceId,
VehicleData vd,
ExtremeValues ev,
BmsSystemStatus ss,
BmsChargeDetail cd,
List<BmsChargeTemperature> chargeTemps,
List<BmsVoltageChannelData> voltageChs,
Date trendBegin,
Date trendEnd,
SimpleDateFormat tf) {
DeviceRealtimeDTO dto = new DeviceRealtimeDTO();
dto.setDeviceId(deviceId);
SystemStatusSection sys = new SystemStatusSection();
if (vd != null) {
sys.setTotalVoltage(vd.getTotalVoltage());
sys.setTotalCurrent(vd.getTotalCurrent());
sys.setSoc(vd.getSoc());
}
if (ev != null) {
sys.setMaxCellVoltage(ev.getMaxVoltageValue());
sys.setMaxCellPos(joinSubsysIndex(ev.getMaxVoltageSubsystemNo(), ev.getMaxVoltageBatteryNo()));
sys.setMinCellVoltage(ev.getMinVoltageValue());
sys.setMinCellPos(joinSubsysIndex(ev.getMinVoltageSubsystemNo(), ev.getMinVoltageBatteryNo()));
sys.setMaxTemp(ev.getMaxTempValue());
sys.setMaxTempPos(joinSubsysIndex(ev.getMaxTempSubsystemNo(), ev.getMaxTempProbeNo()));
sys.setMinTemp(ev.getMinTempValue());
sys.setMinTempPos(joinSubsysIndex(ev.getMinTempSubsystemNo(), ev.getMinTempProbeNo()));
}
dto.setSystemStatus(sys);
RunStatusSection run = new RunStatusSection();
if (vd != null) {
run.setChargingStatus(vd.getChargingStatus());
}
if (ss != null) {
run.setRelayStatus(ss.getRelayStatus());
run.setOnaaccStatus(ss.getOnaaccStatus());
run.setResetCount(ss.getResetCount());
run.setCycleCount(ss.getCycleCount());
run.setSample25v(ss.getSample25v());
run.setSample5v(ss.getSample5v());
}
dto.setRunStatus(run);
ChargeSection chg = new ChargeSection();
if (cd != null) {
chg.setAcChargeCurrent(cd.getAcChargeCurrent());
chg.setAcChargeVoltage(cd.getAcChargeVoltage());
chg.setDcChargeCurrent(cd.getDcChargeCurrent());
chg.setDcChargeVoltage(cd.getDcChargeVoltage());
chg.setChargeTime(cd.getChargeTime());
chg.setAcStopCode(cd.getAcStopCode());
chg.setDcStopCode(cd.getDcStopCode());
chg.setAcCpValue(cd.getAcCpValue());
chg.setAcGunResistance(cd.getAcGunResistance());
chg.setChargingStage(cd.getChargingStage());
chg.setAcStage(cd.getAcStage());
chg.setDcStage(cd.getDcStage());
}
dto.setCharge(chg);
StatisticsSection stat = new StatisticsSection();
if (ss != null) {
stat.setCycleCount(ss.getCycleCount());
}
stat.setTotalRunHours(0L);
stat.setTotalChargeHours(0L);
stat.setTotalDischargeHours(0L);
dto.setStatistics(stat);
dto.setTemperatures(toChargeTemperatureNameValues(chargeTemps, tf));
dto.setVoltages(toVoltageChannelNameValues(voltageChs, tf));
dto.setFaults(new ArrayList<>());
dto.setVoltageTrend(buildRtp1VoltageTrendSeries(deviceId, trendBegin, trendEnd, tf));
dto.setTemperatureTrend(buildT1TemperatureTrendSeries(deviceId, trendBegin, trendEnd, tf));
List<VehicleData> vehicleTrendRows = listVehicleDataForTrend(deviceId, trendBegin, trendEnd);
dto.setTotalVoltageTrend(Collections.singletonList(
buildVehicleNumericTrendSeries(vehicleTrendRows, "总电压(V)", trendBegin, trendEnd, tf, VehicleData::getTotalVoltage)));
dto.setTotalCurrentTrend(Collections.singletonList(
buildVehicleNumericTrendSeries(vehicleTrendRows, "总电流(A)", trendBegin, trendEnd, tf, VehicleData::getTotalCurrent)));
dto.setSocTrend(Collections.singletonList(
buildVehicleNumericTrendSeries(vehicleTrendRows, "SOC(%)", trendBegin, trendEnd, tf,
v -> v.getSoc() == null ? null : BigDecimal.valueOf(v.getSoc()))));
return dto;
}
/** 趋势图DB 侧限量采样,避免 24h 全量拉取 */
private List<VehicleData> listVehicleDataForTrend(String deviceId, Date begin, Date end) {
return vehicleDataService.selectVehicleDataTrendInRange(deviceId, begin, end, REALTIME_TREND_MAX_POINTS);
}
private static Date vehicleTrendPointTime(VehicleData v) {
if (v == null) {
return null;
}
return v.getTimestamp() != null ? v.getTimestamp() : v.getCreateTime();
}
private static LineSeriesDTO buildVehicleNumericTrendSeries(
List<VehicleData> rows,
String seriesName,
Date rangeBegin,
Date rangeEnd,
SimpleDateFormat tf,
Function<VehicleData, BigDecimal> valueFn) {
List<TimePointDTO> points = new ArrayList<>();
if (rows != null) {
for (VehicleData v : rows) {
Date t = vehicleTrendPointTime(v);
if (t == null || t.before(rangeBegin) || t.after(rangeEnd)) {
continue;
}
BigDecimal val = valueFn.apply(v);
if (val == null) {
continue;
}
TimePointDTO p = new TimePointDTO();
p.setTime(tf.format(t));
p.setValue(val);
points.add(p);
}
}
LineSeriesDTO series = new LineSeriesDTO();
series.setName(seriesName);
series.setPoints(points);
return series;
}
private static String joinSubsysIndex(Integer a, Integer b) {
if (a == null || b == null) {
return null;
}
return a + "-" + b;
}
private static List<NameValueDTO> toChargeTemperatureNameValues(List<BmsChargeTemperature> temps, SimpleDateFormat tf) {
if (temps == null || temps.isEmpty()) {
return new ArrayList<>();
}
List<NameValueDTO> out = new ArrayList<>(temps.size());
for (BmsChargeTemperature t : temps) {
NameValueDTO nv = new NameValueDTO();
nv.setName("T" + t.getTempIndex());
nv.setValue(t.getTemperature() == null ? 0 : t.getTemperature().longValue());
nv.setCreateTime(formatTime(t.getCreateTime(), tf));
out.add(nv);
}
return out;
}
private static List<NameValueDTO> toVoltageChannelNameValues(List<BmsVoltageChannelData> chs, SimpleDateFormat tf) {
if (chs == null || chs.isEmpty()) {
return new ArrayList<>();
}
List<NameValueDTO> out = new ArrayList<>(chs.size());
for (BmsVoltageChannelData c : chs) {
NameValueDTO nv = new NameValueDTO();
nv.setName((c.getChannelType() == null ? "CH" : c.getChannelType()) + "-" + c.getChannelIndex());
nv.setValue(c.getVoltage() == null ? 0 : c.getVoltage().longValue());
nv.setCreateTime(formatTime(c.getCreateTime(), tf));
out.add(nv);
}
return out;
}
private static String formatTime(Date d, SimpleDateFormat tf) {
return d == null ? null : tf.format(d);
}
private List<LineSeriesDTO> buildRtp1VoltageTrendSeries(String deviceId, Date begin, Date end, SimpleDateFormat tf) {
List<BmsVoltageChannelData> rows = bmsVoltageChannelDataService.lambdaQuery()
.eq(BmsVoltageChannelData::getDeviceId, deviceId)
.eq(BmsVoltageChannelData::getChannelType, "RTP")
.eq(BmsVoltageChannelData::getChannelIndex, 1)
.isNotNull(BmsVoltageChannelData::getTimestamp)
.ge(BmsVoltageChannelData::getTimestamp, begin)
.le(BmsVoltageChannelData::getTimestamp, end)
.orderByDesc(BmsVoltageChannelData::getTimestamp)
.last("limit " + REALTIME_TREND_MAX_POINTS)
.list();
Collections.reverse(rows);
List<TimePointDTO> points = new ArrayList<>(rows.size());
for (BmsVoltageChannelData it : rows) {
TimePointDTO p = new TimePointDTO();
Date t = it.getTimestamp() != null ? it.getTimestamp() : it.getCreateTime();
p.setTime(formatTime(t, tf));
p.setValue(it.getVoltage());
points.add(p);
}
LineSeriesDTO series = new LineSeriesDTO();
series.setName("RTP-1");
series.setPoints(points);
return Collections.singletonList(series);
}
private List<LineSeriesDTO> buildT1TemperatureTrendSeries(String deviceId, Date begin, Date end, SimpleDateFormat tf) {
List<BmsChargeTemperature> rows = bmsChargeTemperatureService.lambdaQuery()
.eq(BmsChargeTemperature::getDeviceId, deviceId)
.isNotNull(BmsChargeTemperature::getTimestamp)
.ge(BmsChargeTemperature::getTimestamp, begin)
.le(BmsChargeTemperature::getTimestamp, end)
.eq(BmsChargeTemperature::getTempIndex, 1)
.orderByDesc(BmsChargeTemperature::getTimestamp)
.last("limit " + REALTIME_TREND_MAX_POINTS)
.list();
Collections.reverse(rows);
List<TimePointDTO> points = new ArrayList<>(rows.size());
for (BmsChargeTemperature it : rows) {
TimePointDTO p = new TimePointDTO();
Date t = it.getTimestamp() != null ? it.getTimestamp() : it.getCreateTime();
p.setTime(formatTime(t, tf));
p.setValue(it.getTemperature());
points.add(p);
}
LineSeriesDTO series = new LineSeriesDTO();
series.setName("T1");
series.setPoints(points);
return Collections.singletonList(series);
}
/** 并行加载实时快照各表最新数据(默认不含 24h 趋势 SQL */
private RealtimeSnapshot loadRealtimeSnapshot(String deviceId) {
CompletableFuture<VehicleData> vehicleF = CompletableFuture.supplyAsync(
() -> vehicleDataService.selectLatestByDeviceId(deviceId));
CompletableFuture<ExtremeValues> extremeF = CompletableFuture.supplyAsync(
() -> extremeValuesService.selectLatestByDeviceId(deviceId));
CompletableFuture<VehicleLocation> locationF = CompletableFuture.supplyAsync(
() -> vehicleLocationService.selectLatestByDeviceId(deviceId));
CompletableFuture<BmsSystemStatus> systemF = CompletableFuture.supplyAsync(
() -> latestSystemStatus(deviceId));
CompletableFuture<BmsChargeDetail> chargeDetailF = CompletableFuture.supplyAsync(
() -> latestChargeDetail(deviceId));
CompletableFuture<BmsRuntimeData> runtimeF = CompletableFuture.supplyAsync(
() -> latestRuntimeData(deviceId));
CompletableFuture<BmsSopData> sopF = CompletableFuture.supplyAsync(
() -> latestSopData(deviceId));
CompletableFuture<List<SubsystemVoltage>> svF = CompletableFuture.supplyAsync(
() -> listLatestSubsystemVoltages(deviceId));
CompletableFuture<List<SubsystemTemperature>> stF = CompletableFuture.supplyAsync(
() -> listLatestSubsystemTemperatures(deviceId));
CompletableFuture<List<BmsChargeTemperature>> chargeTempF = CompletableFuture.supplyAsync(
() -> listLatestChargeTemperatures(deviceId));
CompletableFuture<List<BmsVoltageChannelData>> voltageChF = CompletableFuture.supplyAsync(
() -> listLatestVoltageChannels(deviceId));
CompletableFuture.allOf(vehicleF, extremeF, locationF, systemF, chargeDetailF, runtimeF, sopF,
svF, stF, chargeTempF, voltageChF).join();
RealtimeSnapshot snap = new RealtimeSnapshot();
snap.vehicleData = vehicleF.join();
snap.extremeValues = extremeF.join();
snap.latestLocation = locationF.join();
snap.systemStatus = systemF.join();
snap.chargeDetail = chargeDetailF.join();
snap.runtimeData = runtimeF.join();
snap.sopData = sopF.join();
snap.subsystemVoltageRows = svF.join();
snap.subsystemTemperatureRows = stF.join();
snap.chargeTemperatures = chargeTempF.join();
snap.voltageChannels = voltageChF.join();
return snap;
}
private static void fillAggregateFromSnapshot(DeviceRealtimeAggregateDTO agg, RealtimeSnapshot snap) {
agg.setVehicleData(snap.vehicleData);
agg.setLatestLocation(snap.latestLocation);
agg.setExtremeValues(snap.extremeValues);
agg.setSystemStatus(snap.systemStatus);
agg.setChargeDetail(snap.chargeDetail);
agg.setRuntimeData(snap.runtimeData);
agg.setSopData(snap.sopData);
agg.setChargeTemperatures(snap.chargeTemperatures);
agg.setVoltageChannels(snap.voltageChannels);
List<SubsystemVoltage> svRows = snap.subsystemVoltageRows;
agg.setSubsystemVoltage(svRows == null || svRows.isEmpty() ? null : svRows.get(0));
agg.setSubsystemVoltages(latestOnePerSubsystem(svRows, SubsystemVoltage::getSubsystemNo));
List<SubsystemTemperature> stRows = snap.subsystemTemperatureRows;
agg.setSubsystemTemperature(stRows == null || stRows.isEmpty() ? null : stRows.get(0));
agg.setSubsystemTemperatures(latestOnePerSubsystem(stRows, SubsystemTemperature::getSubsystemNo));
}
private BmsSystemStatus latestSystemStatus(String deviceId) {
return bmsSystemStatusService.lambdaQuery()
.eq(BmsSystemStatus::getDeviceId, deviceId)
.isNotNull(BmsSystemStatus::getTimestamp)
.orderByDesc(BmsSystemStatus::getTimestamp)
.last("limit 1")
.one();
}
private BmsChargeDetail latestChargeDetail(String deviceId) {
return bmsChargeDetailService.lambdaQuery()
.eq(BmsChargeDetail::getDeviceId, deviceId)
.isNotNull(BmsChargeDetail::getTimestamp)
.orderByDesc(BmsChargeDetail::getTimestamp)
.last("limit 1")
.one();
}
private BmsRuntimeData latestRuntimeData(String deviceId) {
return bmsRuntimeDataService.lambdaQuery()
.eq(BmsRuntimeData::getDeviceId, deviceId)
.isNotNull(BmsRuntimeData::getTimestamp)
.orderByDesc(BmsRuntimeData::getTimestamp)
.last("limit 1")
.one();
}
private BmsSopData latestSopData(String deviceId) {
return bmsSopDataService.lambdaQuery()
.eq(BmsSopData::getDeviceId, deviceId)
.isNotNull(BmsSopData::getTimestamp)
.orderByDesc(BmsSopData::getTimestamp)
.last("limit 1")
.one();
}
private List<SubsystemVoltage> listLatestSubsystemVoltages(String deviceId) {
return subsystemVoltageService.lambdaQuery()
.eq(SubsystemVoltage::getDeviceId, deviceId)
.isNotNull(SubsystemVoltage::getTimestamp)
.orderByDesc(SubsystemVoltage::getTimestamp)
.last("limit " + REALTIME_SUBSYSTEM_PREFETCH)
.list();
}
private List<SubsystemTemperature> listLatestSubsystemTemperatures(String deviceId) {
return subsystemTemperatureService.lambdaQuery()
.eq(SubsystemTemperature::getDeviceId, deviceId)
.isNotNull(SubsystemTemperature::getTimestamp)
.orderByDesc(SubsystemTemperature::getTimestamp)
.last("limit " + REALTIME_SUBSYSTEM_PREFETCH)
.list();
}
private List<BmsChargeTemperature> listLatestChargeTemperatures(String deviceId) {
return bmsChargeTemperatureService.lambdaQuery()
.eq(BmsChargeTemperature::getDeviceId, deviceId)
.isNotNull(BmsChargeTemperature::getTimestamp)
.orderByDesc(BmsChargeTemperature::getTimestamp)
.last("limit 8")
.list();
}
private List<BmsVoltageChannelData> listLatestVoltageChannels(String deviceId) {
return bmsVoltageChannelDataService.lambdaQuery()
.eq(BmsVoltageChannelData::getDeviceId, deviceId)
.isNotNull(BmsVoltageChannelData::getTimestamp)
.orderByDesc(BmsVoltageChannelData::getTimestamp)
.last("limit 16")
.list();
}
private static final class RealtimeSnapshot {
VehicleData vehicleData;
ExtremeValues extremeValues;
VehicleLocation latestLocation;
BmsSystemStatus systemStatus;
BmsChargeDetail chargeDetail;
BmsRuntimeData runtimeData;
BmsSopData sopData;
List<SubsystemVoltage> subsystemVoltageRows;
List<SubsystemTemperature> subsystemTemperatureRows;
List<BmsChargeTemperature> chargeTemperatures;
List<BmsVoltageChannelData> voltageChannels;
}
/** 从按时间倒序的结果中取每个子系统号的第一条(即该子系统最新数据) */
private static <T> List<T> latestOnePerSubsystem(List<T> orderedNewestFirst, Function<T, Integer> subsystemNoFn) {
if (orderedNewestFirst == null || orderedNewestFirst.isEmpty()) {
return Collections.emptyList();
}
Map<Integer, T> seen = new LinkedHashMap<>();
for (T row : orderedNewestFirst) {
Integer no = subsystemNoFn.apply(row);
if (no != null && !seen.containsKey(no)) {
seen.put(no, row);
}
}
List<T> out = new ArrayList<>(seen.values());
out.sort(Comparator.comparing(subsystemNoFn, Comparator.nullsLast(Integer::compareTo)));
return out;
}
}

View File

@ -2,6 +2,8 @@ package com.evobms.project.system.domain;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.builder.ToStringBuilder;
@ -17,43 +19,53 @@ import com.evobms.framework.web.domain.BaseEntity;
*/
@EqualsAndHashCode(callSuper = true)
@Data
@ApiModel(value = "BmsDevices", description = "BBOX设备管理对象")
public class BmsDevices extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键ID */
@ApiModelProperty("主键ID")
private String id;
/** 设备唯一编号 */
@ApiModelProperty("设备唯一编号")
@Excel(name = "设备唯一编号")
private String deviceId;
/** 设备名称 */
@ApiModelProperty("设备名称")
@Excel(name = "设备名称")
private String deviceName;
/** 设备名称 */
@ApiModelProperty("设备SN码")
@Excel(name = "设备SN码")
private String deviceSn;
/** 设备类型 */
@ApiModelProperty("设备类型")
@Excel(name = "设备类型")
private String deviceType;
/** 设备状态 */
@ApiModelProperty("设备状态")
@Excel(name = "设备状态")
private String status;
/** 设备IP地址 */
@ApiModelProperty("设备IP地址")
@Excel(name = "设备IP地址")
private String ipAddress;
/** 固件版本号 */
/** 服务器固件版本号 */
@ApiModelProperty("固件版本号")
@Excel(name = "固件版本号")
private String firmwareVersion;
/** 最后在线时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "最后在线时间", width = 30, dateFormat = "yyyy-MM-dd")
@ApiModelProperty("最后在线时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "最后在线时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date lastOnline;
}

View File

@ -1,19 +1,20 @@
package com.evobms.project.system.mapper;
import java.util.List;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.evobms.project.system.domain.BmsDevices;
/**
* BBOX管理Mapper接口
*
*
* @author 田志阳
* @date 2025-10-10
*/
public interface BmsDevicesMapper
{
public interface BmsDevicesMapper extends BaseMapper<BmsDevices> {
/**
* 查询BBOX管理
*
*
* @param id BBOX管理主键
* @return BBOX管理
*/
@ -21,7 +22,7 @@ public interface BmsDevicesMapper
/**
* 查询BBOX管理列表
*
*
* @param bmsDevices BBOX管理
* @return BBOX管理集合
*/
@ -29,7 +30,7 @@ public interface BmsDevicesMapper
/**
* 新增BBOX管理
*
*
* @param bmsDevices BBOX管理
* @return 结果
*/
@ -37,7 +38,7 @@ public interface BmsDevicesMapper
/**
* 修改BBOX管理
*
*
* @param bmsDevices BBOX管理
* @return 结果
*/
@ -45,7 +46,7 @@ public interface BmsDevicesMapper
/**
* 删除BBOX管理
*
*
* @param id BBOX管理主键
* @return 结果
*/
@ -53,7 +54,7 @@ public interface BmsDevicesMapper
/**
* 批量删除BBOX管理
*
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/

View File

@ -5,15 +5,15 @@ import com.evobms.project.system.domain.BmsDevices;
/**
* BBOX管理Service接口
*
*
* @author 田志阳
* @date 2025-10-10
*/
public interface IBmsDevicesService
public interface IBmsDevicesService
{
/**
* 查询BBOX管理
*
*
* @param id BBOX管理主键
* @return BBOX管理
*/
@ -21,7 +21,7 @@ public interface IBmsDevicesService
/**
* 查询BBOX管理列表
*
*
* @param bmsDevices BBOX管理
* @return BBOX管理集合
*/
@ -29,7 +29,7 @@ public interface IBmsDevicesService
/**
* 新增BBOX管理
*
*
* @param bmsDevices BBOX管理
* @return 结果
*/
@ -37,7 +37,7 @@ public interface IBmsDevicesService
/**
* 修改BBOX管理
*
*
* @param bmsDevices BBOX管理
* @return 结果
*/
@ -45,7 +45,7 @@ public interface IBmsDevicesService
/**
* 批量删除BBOX管理
*
*
* @param ids 需要删除的BBOX管理主键集合
* @return 结果
*/
@ -53,9 +53,13 @@ public interface IBmsDevicesService
/**
* 删除BBOX管理信息
*
*
* @param id BBOX管理主键
* @return 结果
*/
public int deleteBmsDevicesById(String id);
BmsDevices selectBmsDevicesBySn(String deviceSn);
BmsDevices selectBmsDevicesList1(BmsDevices query);
}

View File

@ -1,28 +1,33 @@
package com.evobms.project.system.service.impl;
import java.util.Date;
import java.util.List;
import java.util.Map;
import com.evobms.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evobms.project.system.mapper.BmsDevicesMapper;
import com.evobms.project.system.domain.BmsDevices;
import com.evobms.project.system.service.IBmsDevicesService;
/**
* BBOX管理Service业务层处理
*
*
* @author 田志阳
* @date 2025-10-10
*/
@Service
public class BmsDevicesServiceImpl implements IBmsDevicesService
public class BmsDevicesServiceImpl extends ServiceImpl<BmsDevicesMapper, BmsDevices> implements IBmsDevicesService
{
@Autowired
private BmsDevicesMapper bmsDevicesMapper;
/**
* 查询BBOX管理
*
*
* @param id BBOX管理主键
* @return BBOX管理
*/
@ -34,19 +39,37 @@ public class BmsDevicesServiceImpl implements IBmsDevicesService
/**
* 查询BBOX管理列表
*
*
* @param bmsDevices BBOX管理
* @return BBOX管理
*/
@Override
public List<BmsDevices> selectBmsDevicesList(BmsDevices bmsDevices)
{
return bmsDevicesMapper.selectBmsDevicesList(bmsDevices);
LambdaQueryWrapper<BmsDevices> qw = new LambdaQueryWrapper<>();
if (bmsDevices != null) {
if (bmsDevices.getDeviceId() != null && !bmsDevices.getDeviceId().isEmpty()) {
qw.eq(BmsDevices::getDeviceId, bmsDevices.getDeviceId());
}
Map<String, Object> params = bmsDevices.getParams();
if (params != null) {
Date begin = DateUtils.parseDate(params.get("beginTime"));
Date end = DateUtils.parseDate(params.get("endTime"));
if (begin != null) {
qw.and(w -> w.ge(BmsDevices::getUpdateTime, begin).or().ge(BmsDevices::getCreateTime, begin));
}
if (end != null) {
qw.and(w -> w.le(BmsDevices::getUpdateTime, end).or().le(BmsDevices::getCreateTime, end));
}
}
}
qw.orderByDesc(BmsDevices::getUpdateTime).orderByDesc(BmsDevices::getCreateTime);
return bmsDevicesMapper.selectList(qw);
}
/**
* 新增BBOX管理
*
*
* @param bmsDevices BBOX管理
* @return 结果
*/
@ -59,12 +82,12 @@ public class BmsDevicesServiceImpl implements IBmsDevicesService
/**
* 修改BBOX管理
*
*
* @param bmsDevices BBOX管理
* @return 结果
*/
@Override
public int updateBmsDevices(BmsDevices bmsDevices)
public int updateBmsDevices(BmsDevices bmsDevices)
{
bmsDevices.setUpdateTime(DateUtils.getNowDate());
return bmsDevicesMapper.updateBmsDevices(bmsDevices);
@ -72,7 +95,7 @@ public class BmsDevicesServiceImpl implements IBmsDevicesService
/**
* 批量删除BBOX管理
*
*
* @param ids 需要删除的BBOX管理主键
* @return 结果
*/
@ -84,7 +107,7 @@ public class BmsDevicesServiceImpl implements IBmsDevicesService
/**
* 删除BBOX管理信息
*
*
* @param id BBOX管理主键
* @return 结果
*/
@ -93,4 +116,22 @@ public class BmsDevicesServiceImpl implements IBmsDevicesService
{
return bmsDevicesMapper.deleteBmsDevicesById(id);
}
@Override
public BmsDevices selectBmsDevicesBySn(String deviceSn) {
LambdaQueryWrapper<BmsDevices> bmslq = new LambdaQueryWrapper<>();
bmslq.eq(BmsDevices::getDeviceSn, deviceSn);
return baseMapper.selectOne(bmslq);
}
@Override
public BmsDevices selectBmsDevicesList1(BmsDevices bmsDevices) {
LambdaQueryWrapper<BmsDevices> eq = new LambdaQueryWrapper<>();
if (bmsDevices != null) {
eq.eq(BmsDevices::getDeviceSn, bmsDevices.getDeviceSn());
return baseMapper.selectOne(eq);
}
return null;
}
}

View File

@ -0,0 +1,476 @@
package com.evobms.project.vehicledata.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.evobms.framework.web.domain.R;
import com.evobms.project.battery.domain.ExtremeValues;
import com.evobms.project.battery.domain.SubsystemTemperature;
import com.evobms.project.battery.domain.SubsystemVoltage;
import com.evobms.project.battery.service.IExtremeValuesService;
import com.evobms.project.battery.service.ISubsystemTemperatureService;
import com.evobms.project.battery.service.ISubsystemVoltageService;
import com.evobms.project.system.domain.BmsDevices;
import com.evobms.project.system.mapper.BmsDevicesMapper;
import com.evobms.project.vehicledata.domain.HomeDashboardDTO;
import com.evobms.project.vehicledata.domain.VehicleData;
import com.evobms.project.vehicledata.domain.VehicleLocation;
import com.evobms.project.vehicledata.dto.*;
import com.evobms.project.vehicledata.service.IVehicleDataService;
import com.evobms.project.vehicledata.service.IVehicleLocationService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 首页数据Controller
*
* @author 田志阳
* @date 2025-11-15
*/
@RestController
@Api(tags = "首页数据统计")
public class BatteryChartController {
@Autowired
private IVehicleDataService vehicleDataService;
@Autowired
private IVehicleLocationService vehicleLocationService;
@Autowired
private ISubsystemVoltageService subsystemVoltageService;
@Autowired
private ISubsystemTemperatureService subsystemTemperatureService;
@Autowired
private IExtremeValuesService extremeValuesService;
@Autowired
private BmsDevicesMapper bmsDevicesMapper;
// 图表数据按最近 hours 小时返回整车电压/电流/SOC 的时间序列
@GetMapping("/battery-data/{deviceId}/chart")
@ApiOperation(value = "-整车电压电流SOC时间序列", notes = "按最近hours小时返回时间序列")
public List<ChartDataPoint> getChartData(@ApiParam(value = "设备ID", required = true) @PathVariable("deviceId") String deviceId, @ApiParam(value = "最近多少小时的数据", defaultValue = "24") @RequestParam(value = "hours", required = false, defaultValue = "24") int hours) {
long millis = System.currentTimeMillis() - (long) hours * 3600_000L;
Date startTime = new Date(millis);
List<VehicleData> rows = vehicleDataService.selectByDeviceAndStartTime(deviceId, startTime);
return rows.stream().map(v -> new ChartDataPoint(v.getTimestamp(), v.getTotalVoltage() == null ? 0 : v.getTotalVoltage().doubleValue(), v.getTotalCurrent() == null ? 0 : v.getTotalCurrent().doubleValue(), v.getSoc() == null ? 0 : v.getSoc())).collect(Collectors.toList());
}
// 整车最新返回 deviceId 最新的一条整车数据
@GetMapping("/vehicle-data/{deviceId}/latest")
@ApiOperation(value = "-整车最新数据", notes = "返回指定设备最新整车数据")
public VehicleData latestVehicleData(@ApiParam(value = "设备ID", required = true) @PathVariable("deviceId") String deviceId) {
return vehicleDataService.selectLatestByDeviceId(deviceId);
}
// 位置最新返回 deviceId 最新的一条位置数据
@GetMapping("/vehicle-location/{deviceId}/latest")
@ApiOperation(value = "-车辆最新位置", notes = "返回指定设备最新位置数据")
public VehicleLocation latestVehicleLocation(@ApiParam(value = "设备ID", required = true) @PathVariable("deviceId") String deviceId) {
return vehicleLocationService.selectLatestByDeviceId(deviceId);
}
// 位置轨迹按时间范围返回位置列表start/end 为毫秒时间戳可选 limit 限制条数
@GetMapping("/vehicle-location/{deviceId}")
@ApiOperation(value = "-车辆位置轨迹", notes = "按时间范围返回位置列表")
public List<VehicleLocation> locationRange(@ApiParam(value = "设备ID", required = true) @PathVariable("deviceId") String deviceId, @ApiParam(value = "开始时间戳(毫秒)", required = true) @RequestParam("start") long startMillis, @ApiParam(value = "结束时间戳(毫秒)", required = true) @RequestParam("end") long endMillis, @ApiParam(value = "限制返回条数") @RequestParam(value = "limit", required = false) Integer limit) {
Date start = new Date(startMillis);
Date end = new Date(endMillis);
return vehicleLocationService.selectByDeviceAndRange(deviceId, start, end, limit);
}
// 子系统最新帧电压返回指定子系统号最新的一帧电压数据
@GetMapping("/subsystem-voltage/{deviceId}/{subsystemNo}/latest")
@ApiOperation(value = "-子系统最新电压帧", notes = "返回指定子系统最新电压数据")
public SubsystemVoltage latestVoltage(@ApiParam(value = "设备ID", required = true) @PathVariable("deviceId") String deviceId, @ApiParam(value = "子系统号", required = true) @PathVariable("subsystemNo") Integer subsystemNo) {
return subsystemVoltageService.selectLatestByDeviceAndSubsystem(deviceId, subsystemNo);
}
// 子系统最新帧温度返回指定子系统号最新的一帧温度数据
@GetMapping("/subsystem-temperature/{deviceId}/{subsystemNo}/latest")
@ApiOperation(value = "-子系统最新温度帧", notes = "返回指定子系统最新温度数据")
public SubsystemTemperature latestTemperature(@ApiParam(value = "设备ID", required = true) @PathVariable("deviceId") String deviceId, @ApiParam(value = "子系统号", required = true) @PathVariable("subsystemNo") Integer subsystemNo) {
return subsystemTemperatureService.selectLatestByDeviceAndSubsystem(deviceId, subsystemNo);
}
@GetMapping("/extreme-values/{deviceId}/latest")
@ApiOperation(value = "-极值最新数据", notes = "返回指定设备极值数据支持before筛选")
public ExtremeValues latestExtremeValues(@ApiParam(value = "设备ID", required = true) @PathVariable("deviceId") String deviceId, @ApiParam(value = "筛选指定时间之前的数据(yyyy-MM-dd HH:mm:ss 或 毫秒时间戳)") @RequestParam(value = "before", required = false) String before) {
if (before == null || before.isEmpty()) {
return extremeValuesService.selectLatestByDeviceId(deviceId);
}
Date beforeTime = null;
if (before.matches("\\d+")) {
beforeTime = new Date(Long.parseLong(before));
} else {
try {
beforeTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(before);
} catch (ParseException e) {
return extremeValuesService.selectLatestByDeviceId(deviceId);
}
}
return extremeValuesService.selectLatestByDeviceIdBefore(deviceId, beforeTime);
}
@GetMapping("/api/dashboard/home")
@ApiOperation(value = "-首页所有分布数据", notes = "获取首页大屏的分布统计数据")
public R<HomeDashboardDTO> home() {
HomeDashboardDTO dto = vehicleDataService.getHomeDashboard();
return R.ok(dto);
}
/* @GetMapping("/monitor/dashboard/summary")
@ApiOperation(value = "首页卡片统计", notes = "返回项目数、设备数、在线数、故障数")
public R<DashboardSummaryDTO> summary() {
long deviceCount = bmsDevicesMapper.selectCount(null);
long onlineCount = bmsDevicesMapper.selectCount(new QueryWrapper<BmsDevices>().eq("status", "online"));
long projectCount = 22L;
long faultCount = 12L;
DashboardSummaryDTO dto = new DashboardSummaryDTO();
dto.setProjectCount(projectCount);
dto.setDeviceCount(deviceCount);
dto.setOnlineCount(onlineCount);
dto.setFaultCount(faultCount);
return R.ok(dto);
}*/
/* @GetMapping("/monitor/dashboard/soc-distribution")
@ApiOperation(value = "设备SOC分布", notes = "按每台设备最新SOC值统计")
public R<List<NameValueDTO>> socDistribution(@ApiParam(value = "分桶边界逗号分隔0,20,40,60,80,100") @RequestParam(value = "bins", required = false, defaultValue = "0,20,40,60,80,100") String bins, @ApiParam(value = "是否包含离线设备", defaultValue = "false") @RequestParam(value = "includeOffline", required = false, defaultValue = "false") boolean includeOffline) {
List<Integer> edges = Stream.of(bins.split(",")).map(String::trim).filter(s -> !s.isEmpty()).map(Integer::parseInt).sorted().toList();
if (edges.size() < 2) {
return R.ok(new ArrayList<>());
}
long[] counts = new long[edges.size() - 1];
QueryWrapper<BmsDevices> qw = new QueryWrapper<>();
if (!includeOffline) {
//查询在线设备
qw.eq("status", "online");
}
List<BmsDevices> devices = bmsDevicesMapper.selectList(qw);
for (BmsDevices d : devices) {
String deviceId = d.getDeviceId();
if (deviceId == null || deviceId.isEmpty()) {
continue;
}
VehicleData latest = vehicleDataService.selectLatestByDeviceId(deviceId);
Integer soc = latest == null ? null : latest.getSoc();
//过滤
if (soc == null || soc < 0 || soc > 100) {
continue;
}
for (int i = 0; i < counts.length; i++) {
int min = edges.get(i);
int max = edges.get(i + 1);
boolean isLast = (i == counts.length - 1);
boolean inRange = isLast ? (soc >= min && soc <= max) : (soc >= min && soc < max);
if (inRange) {
counts[i]++;
break;
}
}
}
List<NameValueDTO> items = new ArrayList<>(counts.length);
String ts = Instant.now().toString();
for (int i = 0; i < counts.length; i++) {
int min = edges.get(i);
int max = edges.get(i + 1);
NameValueDTO nv = new NameValueDTO();
nv.setName(min + "-" + max + "%");
nv.setValue(counts[i]);
nv.setCreateTime(ts);
items.add(nv);
}
return R.ok(items);
}
*/
@GetMapping("/monitor/dashboard/overview")
@ApiOperation(value = "首页概览聚合", notes = "返回基础统计、SOC分布、日充放电趋势")
public R<DashboardOverviewDTO> overview(@ApiParam(value = "SOC分桶默认 0,20,40,60,80,100") @RequestParam(value = "bins", required = false, defaultValue = "0,20,40,60,80,100") String bins, @ApiParam(value = "趋势统计天数", defaultValue = "7") @RequestParam(value = "days", required = false, defaultValue = "7") int days, @ApiParam(value = "运行时长档位(小时)", defaultValue = "10,50,100,150,200,250,300") @RequestParam(value = "runtimeBins", required = false, defaultValue = "10,50,100,150,200,250,300") String runtimeBins) {
DashboardOverviewDTO out = new DashboardOverviewDTO();
// summary
long deviceCount = bmsDevicesMapper.selectCount(null);
long onlineCount = bmsDevicesMapper.selectCount(new QueryWrapper<BmsDevices>().eq("status", "online"));
long projectCount = 22L;
long faultCount = 12L;
DashboardSummaryDTO s = new DashboardSummaryDTO();
s.setProjectCount(projectCount);
s.setDeviceCount(deviceCount);
s.setOnlineCount(onlineCount);
s.setFaultCount(faultCount);
out.setSummary(s);
// soc distribution
List<Integer> edges = Stream.of(bins.split(",")).map(String::trim).filter(x -> !x.isEmpty()).map(Integer::parseInt).sorted().toList();
long[] counts = new long[Math.max(0, edges.size() - 1)];
if (counts.length > 0) {
List<BmsDevices> devices = bmsDevicesMapper.selectList(new QueryWrapper<BmsDevices>().eq("status", "online"));
for (BmsDevices d : devices) {
String did = d.getDeviceId();
if (did == null || did.isEmpty()) continue;
VehicleData latest = vehicleDataService.selectLatestByDeviceId(did);
Integer soc = latest == null ? null : latest.getSoc();
if (soc == null || soc < 0 || soc > 100) continue;
for (int i = 0; i < counts.length; i++) {
int min = edges.get(i), max = edges.get(i + 1);
boolean last = i == counts.length - 1;
boolean in = last ? soc >= min && soc <= max : soc >= min && soc < max;
if (in) {
counts[i]++;
break;
}
}
}
}
String ts = Instant.now().toString();
List<NameValueDTO> pie = new ArrayList<>();
for (int i = 0; i < counts.length; i++) {
int min = edges.get(i), max = edges.get(i + 1);
NameValueDTO nv = new NameValueDTO();
nv.setName(min + "-" + max + "%");
nv.setValue(counts[i]);
nv.setCreateTime(ts);
pie.add(nv);
}
out.setSocDistribution(pie);
// trend (simple heuristic by VehicleData records)
int safeDays = Math.max(1, Math.min(31, days));
Date endTime = new Date();
Date startTime = Date.from(Instant.ofEpochMilli(endTime.getTime()).minusSeconds(safeDays * 86400L));
// 复用 selectVehicleDataList 进行时间范围查询
VehicleData q = new VehicleData();
Map<String, Object> params = new HashMap<>();
params.put("beginTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(startTime));
params.put("endTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(endTime));
q.setParams(params);
List<VehicleData> all = vehicleDataService.selectVehicleDataList(q);
all.sort((a, b) -> {
if (a.getDeviceId() == null)
return -1;
if (b.getDeviceId() == null)
return 1;
int c = a.getDeviceId().compareTo(b.getDeviceId());
if (c != 0) return c;
return a.getTimestamp().compareTo(b.getTimestamp());
});
Map<String, DashboardTrendItemDTO> byDate = new HashMap<>();
SimpleDateFormat dayFmt = new SimpleDateFormat("yyyy-MM-dd");
// 初始化天数
LocalDate today = LocalDate.now();
for (int i = safeDays - 1; i >= 0; i--) {
LocalDate d = today.minusDays(i);
DashboardTrendItemDTO item = new DashboardTrendItemDTO();
item.setDate(d.toString());
item.setChargeDuration(0);
item.setDischargeDuration(0);
byDate.put(d.toString(), item);
}
// 累加相邻样本间隔
for (int i = 0; i < all.size() - 1; i++) {
VehicleData cur = all.get(i);
VehicleData nex = all.get(i + 1);
if (cur.getDeviceId() == null || !cur.getDeviceId().equals(nex.getDeviceId())) continue;
if (cur.getTimestamp() == null || nex.getTimestamp() == null) continue;
long delta = (nex.getTimestamp().getTime() - cur.getTimestamp().getTime()) / 1000L;
if (delta <= 0 || delta > 6 * 3600) continue; // 限制单段不超过6小时
String key = dayFmt.format(cur.getTimestamp());
DashboardTrendItemDTO item = byDate.get(key);
if (item == null) continue;
Integer st = cur.getChargingStatus();
if (st != null && (st == 1 || st == 2)) {
item.setChargeDuration(item.getChargeDuration() + delta);
} else if (st != null && st == 3) {
item.setDischargeDuration(item.getDischargeDuration() + delta);
}
}
List<DashboardTrendItemDTO> trend = byDate.values().stream().sorted(java.util.Comparator.comparing(DashboardTrendItemDTO::getDate)).collect(Collectors.toList());
out.setTrend(trend);
// runtime summary + distribution
long sumChargeSec = trend.stream().mapToLong(DashboardTrendItemDTO::getChargeDuration).sum();
long sumDischargeSec = trend.stream().mapToLong(DashboardTrendItemDTO::getDischargeDuration).sum();
RuntimeSummaryDTO rsum = new RuntimeSummaryDTO();
rsum.setTotalChargeHours(sumChargeSec / 3600);
rsum.setTotalDischargeHours(sumDischargeSec / 3600);
rsum.setTotalRunHours((sumChargeSec + sumDischargeSec) / 3600);
rsum.setTotalChargeEnergyKWh(0);
rsum.setTotalDischargeEnergyKWh(0);
rsum.setTotalChargeCapacityAh(0);
rsum.setTotalDischargeCapacityAh(0);
out.setRuntimeSummary(rsum);
// 每设备运行小时 days chargingStatus in (1,2,3) 累加
Map<String, Long> runSecByDevice = new HashMap<>();
for (int i = 0; i < all.size() - 1; i++) {
VehicleData cur = all.get(i);
VehicleData nex = all.get(i + 1);
if (cur.getDeviceId() == null || !cur.getDeviceId().equals(nex.getDeviceId())) continue;
if (cur.getTimestamp() == null || nex.getTimestamp() == null) continue;
long delta = (nex.getTimestamp().getTime() - cur.getTimestamp().getTime()) / 1000L;
if (delta <= 0 || delta > 6 * 3600) continue;
Integer st = cur.getChargingStatus();
if (st != null && (st == 1 || st == 2 || st == 3)) {
runSecByDevice.merge(cur.getDeviceId(), delta, Long::sum);
}
}
List<Integer> hb = Stream.of(runtimeBins.split(",")).map(String::trim).filter(x -> !x.isEmpty()).map(Integer::parseInt).sorted().toList();
long[] hCounts = new long[Math.max(0, hb.size() - 1)];
for (Map.Entry<String, Long> e : runSecByDevice.entrySet()) {
long hours = e.getValue() / 3600;
for (int i = 0; i < hCounts.length; i++) {
int min = hb.get(i), max = hb.get(i + 1);
boolean last = i == hCounts.length - 1;
boolean in = last ? hours >= min && hours <= max : hours >= min && hours < max;
if (in) { hCounts[i]++; break; }
}
}
List<NameValueDTO> runtimeDist = new ArrayList<>();
String now = Instant.now().toString();
for (int i = 0; i < hCounts.length; i++) {
int min = hb.get(i), max = hb.get(i + 1);
NameValueDTO nv = new NameValueDTO();
nv.setName(min + "h");
nv.setValue(hCounts[i]);
nv.setCreateTime(now);
runtimeDist.add(nv);
}
out.setRuntimeDuration(runtimeDist);
return R.ok(out);
}
@GetMapping("/monitor/device/latest-locations")
@ApiOperation(value = "所有设备最新位置", notes = "返回每个设备的设备对象与其最新位置")
public R<List<DeviceLatestLocationDTO>> latestLocations(@ApiParam(value = "是否仅包含在线设备", defaultValue = "false") @RequestParam(value = "onlineOnly", required = false, defaultValue = "false") boolean onlineOnly) {
QueryWrapper<BmsDevices> qw = new QueryWrapper<>();
if (onlineOnly) {
qw.eq("status", "online");
}
List<BmsDevices> devices = bmsDevicesMapper.selectList(qw);
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
List<DeviceLatestLocationDTO> result = new ArrayList<>(devices.size());
for (BmsDevices d : devices) {
String did = d.getDeviceId();
VehicleLocation loc = did == null ? null : vehicleLocationService.selectLatestByDeviceId(did);
TrackPointDTO p = null;
if (loc != null) {
p = new TrackPointDTO();
p.setTimestamp(loc.getTimestamp() == null ? null : fmt.format(loc.getTimestamp()));
p.setLongitude(loc.getLongitude() == null ? null : loc.getLongitude().doubleValue());
p.setLatitude(loc.getLatitude() == null ? null : loc.getLatitude().doubleValue());
p.setPositioningStatus(loc.getPositioningStatus());
}
DeviceLatestLocationDTO dto = new DeviceLatestLocationDTO();
dto.setDevice(d);
dto.setLocation(p);
result.add(dto);
}
return R.ok(result);
}
@GetMapping("/monitor/device/{deviceId}/track")
@ApiOperation(value = "设备轨迹", notes = "返回指定时间范围内的轨迹")
public R<DeviceTrackDTO> deviceTrack(@ApiParam(value = "设备ID", required = true) @PathVariable("deviceId") String deviceId, @ApiParam(value = "开始时间戳(毫秒)", required = true) @RequestParam("start") long startMillis, @ApiParam(value = "结束时间戳(毫秒)", required = true) @RequestParam("end") long endMillis, @ApiParam(value = "限制返回条数", defaultValue = "1000") @RequestParam(value = "limit", required = false, defaultValue = "1000") Integer limit, @ApiParam(value = "是否包含无效定位", defaultValue = "false") @RequestParam(value = "includeInvalid", required = false, defaultValue = "false") boolean includeInvalid) {
Date start = new Date(startMillis);
Date end = new Date(endMillis);
//查询车辆位置信息
List<VehicleLocation> rows = vehicleLocationService.selectByDeviceAndRange(deviceId, start, end, limit);
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//位置列表按 timestamp 升序排序空时间的记录排在最后
List<TrackPointDTO> list = rows.stream()
.filter(v -> includeInvalid || (v.getPositioningStatus() != null && v.getPositioningStatus() == 1))
.sorted((a, b) -> {
Date ta = a.getTimestamp(), tb = b.getTimestamp();
if (ta == null && tb == null) return 0;
if (ta == null) return 1;
if (tb == null) return -1;
return ta.compareTo(tb);
})
.map(v -> {
TrackPointDTO p = new TrackPointDTO();
p.setTimestamp(v.getTimestamp() == null ? null : fmt.format(v.getTimestamp()));
p.setLongitude(v.getLongitude() == null ? null : v.getLongitude().doubleValue());
p.setLatitude(v.getLatitude() == null ? null : v.getLatitude().doubleValue());
p.setPositioningStatus(v.getPositioningStatus());
return p;
}).collect(Collectors.toList());
DeviceTrackDTO dto = new DeviceTrackDTO();
dto.setDeviceId(deviceId);
// 基本设备信息
BmsDevices dev = bmsDevicesMapper.selectOne(new QueryWrapper<BmsDevices>().eq("device_id", deviceId));
if (dev != null) {
dto.setDeviceName(dev.getDeviceName());
dto.setDeviceSn(dev.getDeviceSn());
dto.setDeviceType(dev.getDeviceType());
dto.setStatus(dev.getStatus());
dto.setFirmwareVersion(dev.getFirmwareVersion());
dto.setLastOnline(dev.getLastOnline() == null ? null : fmt.format(dev.getLastOnline()));
}
// 运行时指标取最新整车数据
VehicleData latest = vehicleDataService.selectLatestByDeviceId(deviceId);
if (latest != null) {
dto.setVehicleSpeed(latest.getVehicleSpeed());
dto.setTotalMileage(latest.getTotalMileage());
dto.setTotalVoltage(latest.getTotalVoltage());
dto.setTotalCurrent(latest.getTotalCurrent());
dto.setSoc(latest.getSoc());
}
dto.setTrackList(list);
return R.ok(dto);
}
@GetMapping("/monitor/device/{deviceId}/detail")
@ApiOperation(value = "设备详细信息", notes = "聚合设备、整车、位置等信息")
public R<DeviceDetailDTO> deviceDetail(@ApiParam(value = "设备ID", required = true) @PathVariable("deviceId") String deviceId) {
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
DeviceDetailDTO out = new DeviceDetailDTO();
out.setDeviceId(deviceId);
BmsDevices dev = bmsDevicesMapper.selectOne(new QueryWrapper<BmsDevices>().eq("device_id", deviceId));
if (dev != null) {
out.setDeviceName(dev.getDeviceName());
out.setDeviceSn(dev.getDeviceSn());
out.setDeviceType(dev.getDeviceType());
out.setStatus(dev.getStatus());
out.setFirmwareVersion(dev.getFirmwareVersion());
out.setLastOnline(dev.getLastOnline() == null ? null : fmt.format(dev.getLastOnline()));
}
VehicleData v = vehicleDataService.selectLatestByDeviceId(deviceId);
if (v != null) {
out.setTotalVoltage(v.getTotalVoltage());
out.setTotalCurrent(v.getTotalCurrent());
out.setSoc(v.getSoc());
out.setVehicleStatus(v.getVehicleStatus());
out.setChargingStatus(v.getChargingStatus());
out.setOperationMode(v.getOperationMode());
out.setDcdcStatus(v.getDcdcStatus());
out.setGearPosition(v.getGearPosition());
out.setInsulationResistance(v.getInsulationResistance());
}
VehicleLocation loc = vehicleLocationService.selectLatestByDeviceId(deviceId);
if (loc != null) {
out.setLongitude(loc.getLongitude() == null ? null : loc.getLongitude().doubleValue());
out.setLatitude(loc.getLatitude() == null ? null : loc.getLatitude().doubleValue());
out.setLocationTime(loc.getTimestamp() == null ? null : fmt.format(loc.getTimestamp()));
out.setPositioningStatus(loc.getPositioningStatus());
}
out.setLastMaintenanceTime(null);
out.setLastUpgradeTime(null);
return R.ok(out);
}
}

View File

@ -0,0 +1,113 @@
package com.evobms.project.vehicledata.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.evobms.framework.aspectj.lang.annotation.Log;
import com.evobms.framework.aspectj.lang.enums.BusinessType;
import com.evobms.project.vehicledata.domain.VehicleData;
import com.evobms.project.vehicledata.service.IVehicleDataService;
import com.evobms.framework.web.controller.BaseController;
import com.evobms.framework.web.domain.AjaxResult;
import com.evobms.common.utils.poi.ExcelUtil;
import com.evobms.framework.web.page.TableDataInfo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
/**
* 整车数据Controller
*
* @author 田志阳
* @date 2025-11-15
*/
@RestController
@RequestMapping("/VehicleData/VehicleData")
@Api(tags = "整车数据管理")
public class VehicleDataController extends BaseController
{
@Autowired
private IVehicleDataService vehicleDataService;
/**
* 查询整车数据列表
*/
@PreAuthorize("@ss.hasPermi('VehicleData:VehicleData:list')")
@GetMapping("/list")
@ApiOperation("查询整车数据列表")
public TableDataInfo list(VehicleData vehicleData)
{
startPage();
List<VehicleData> list = vehicleDataService.selectVehicleDataList(vehicleData);
return getDataTable(list);
}
/**
* 导出整车数据列表
*/
@PreAuthorize("@ss.hasPermi('VehicleData:VehicleData:export')")
@Log(title = "整车数据", businessType = BusinessType.EXPORT)
@PostMapping("/export")
@ApiOperation("导出整车数据列表")
public void export(HttpServletResponse response, VehicleData vehicleData)
{
List<VehicleData> list = vehicleDataService.selectVehicleDataList(vehicleData);
ExcelUtil<VehicleData> util = new ExcelUtil<VehicleData>(VehicleData.class);
util.exportExcel(response, list, "整车数据数据");
}
/**
* 获取整车数据详细信息
*/
@PreAuthorize("@ss.hasPermi('VehicleData:VehicleData:query')")
@GetMapping(value = "/{id}")
@ApiOperation("获取整车数据详细信息")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(vehicleDataService.selectVehicleDataById(id));
}
/**
* 新增整车数据
*/
@PreAuthorize("@ss.hasPermi('VehicleData:VehicleData:add')")
@Log(title = "整车数据", businessType = BusinessType.INSERT)
@PostMapping
@ApiOperation("新增整车数据")
public AjaxResult add(@RequestBody VehicleData vehicleData)
{
return toAjax(vehicleDataService.insertVehicleData(vehicleData));
}
/**
* 修改整车数据
*/
@PreAuthorize("@ss.hasPermi('VehicleData:VehicleData:edit')")
@Log(title = "整车数据", businessType = BusinessType.UPDATE)
@PutMapping
@ApiOperation("修改整车数据")
public AjaxResult edit(@RequestBody VehicleData vehicleData)
{
return toAjax(vehicleDataService.updateVehicleData(vehicleData));
}
/**
* 删除整车数据
*/
@PreAuthorize("@ss.hasPermi('VehicleData:VehicleData:remove')")
@Log(title = "整车数据", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
@ApiOperation("删除整车数据")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(vehicleDataService.deleteVehicleDataByIds(ids));
}
}

View File

@ -0,0 +1,113 @@
package com.evobms.project.vehicledata.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.evobms.framework.aspectj.lang.annotation.Log;
import com.evobms.framework.aspectj.lang.enums.BusinessType;
import com.evobms.project.vehicledata.domain.VehicleLocation;
import com.evobms.project.vehicledata.service.IVehicleLocationService;
import com.evobms.framework.web.controller.BaseController;
import com.evobms.framework.web.domain.AjaxResult;
import com.evobms.common.utils.poi.ExcelUtil;
import com.evobms.framework.web.page.TableDataInfo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
/**
* 车辆位置数据Controller
*
* @author ruoyi
* @date 2025-11-15
*/
@RestController
@RequestMapping("/location/location")
@Api(tags = "车辆位置管理")
public class VehicleLocationController extends BaseController
{
@Autowired
private IVehicleLocationService vehicleLocationService;
/**
* 查询车辆位置数据列表
*/
@PreAuthorize("@ss.hasPermi('location:location:list')")
@GetMapping("/list")
@ApiOperation("查询车辆位置列表")
public TableDataInfo list(VehicleLocation vehicleLocation)
{
startPage();
List<VehicleLocation> list = vehicleLocationService.selectVehicleLocationList(vehicleLocation);
return getDataTable(list);
}
/**
* 导出车辆位置数据列表
*/
@PreAuthorize("@ss.hasPermi('location:location:export')")
@Log(title = "车辆位置数据", businessType = BusinessType.EXPORT)
@PostMapping("/export")
@ApiOperation("导出车辆位置列表")
public void export(HttpServletResponse response, VehicleLocation vehicleLocation)
{
List<VehicleLocation> list = vehicleLocationService.selectVehicleLocationList(vehicleLocation);
ExcelUtil<VehicleLocation> util = new ExcelUtil<VehicleLocation>(VehicleLocation.class);
util.exportExcel(response, list, "车辆位置数据数据");
}
/**
* 获取车辆位置数据详细信息
*/
@PreAuthorize("@ss.hasPermi('location:location:query')")
@GetMapping(value = "/{id}")
@ApiOperation("获取车辆位置详细信息")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(vehicleLocationService.selectVehicleLocationById(id));
}
/**
* 新增车辆位置数据
*/
@PreAuthorize("@ss.hasPermi('location:location:add')")
@Log(title = "车辆位置数据", businessType = BusinessType.INSERT)
@PostMapping
@ApiOperation("新增车辆位置")
public AjaxResult add(@RequestBody VehicleLocation vehicleLocation)
{
return toAjax(vehicleLocationService.insertVehicleLocation(vehicleLocation));
}
/**
* 修改车辆位置数据
*/
@PreAuthorize("@ss.hasPermi('location:location:edit')")
@Log(title = "车辆位置数据", businessType = BusinessType.UPDATE)
@PutMapping
@ApiOperation("修改车辆位置")
public AjaxResult edit(@RequestBody VehicleLocation vehicleLocation)
{
return toAjax(vehicleLocationService.updateVehicleLocation(vehicleLocation));
}
/**
* 删除车辆位置数据
*/
@PreAuthorize("@ss.hasPermi('location:location:remove')")
@Log(title = "车辆位置数据", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
@ApiOperation("删除车辆位置")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(vehicleLocationService.deleteVehicleLocationByIds(ids));
}
}

View File

@ -0,0 +1,28 @@
package com.evobms.project.vehicledata.domain;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Map;
@Data
@ApiModel(description = "首页仪表盘数据传输对象")
public class HomeDashboardDTO {
@ApiModelProperty(value = "设备总数")
private Long deviceCount; // 设备总数
@ApiModelProperty(value = "在线设备数")
private Integer onlineCount; // 在线设备数
@ApiModelProperty(value = "平均 SOC")
private Double avgSoc; // 平均 SOC
@ApiModelProperty(value = "当前最高温度")
private Double maxTemp; // 当前最高温度
@ApiModelProperty(value = "最大单体压差")
private Double maxVoltageDiff; // 最大单体压差
@ApiModelProperty(value = "未恢复告警数")
private Integer alarmCount; // 未恢复告警数
@ApiModelProperty(value = "SOC 分布")
private Map<String, Integer> socDistribution; // SOC 分布
@ApiModelProperty(value = "温度分布")
private Map<String, Integer> tempDistribution; // 温度分布
}

View File

@ -0,0 +1,104 @@
package com.evobms.project.vehicledata.domain;
import java.math.BigDecimal;
import java.util.Date;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.evobms.framework.aspectj.lang.annotation.Excel;
import com.evobms.framework.web.domain.BaseEntity;
/**
* 整车数据对象 vehicle_data
*
* @author 田志阳
* @date 2025-11-15
*/
@ApiModel(description = "整车数据对象")
@Data
@EqualsAndHashCode(callSuper = true)
public class VehicleData extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键ID */
@ApiModelProperty(value = "主键ID")
private Long id;
/** 设备ID */
@ApiModelProperty(value = "设备ID")
@Excel(name = "设备ID")
private String deviceId;
/** 数据时间戳 */
@ApiModelProperty(value = "数据时间戳")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "数据时间戳", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date timestamp;
/** 车辆状态 (1:启动 2:熄火 3:其他 254:异常 255:无效) */
@ApiModelProperty(value = "车辆状态 (1:启动 2:熄火 3:其他 254:异常 255:无效)")
@Excel(name = "车辆状态 ")
private Integer vehicleStatus;
/** 充电状态 (1:停车充电 2:行驶充电 3:未充电 4:充电完成 254:异常 255:无效) */
@ApiModelProperty(value = "充电状态 (1:停车充电 2:行驶充电 3:未充电 4:充电完成 254:异常 255:无效)")
@Excel(name = "充电状态 ")
private Integer chargingStatus;
/** 运行模式 (1:纯电 2:混动 3:燃油 254:异常 255:无效) */
@ApiModelProperty(value = "运行模式 (1:纯电 2:混动 3:燃油 254:异常 255:无效)")
@Excel(name = "运行模式 ")
private Integer operationMode;
/** 车速(km/h) */
@ApiModelProperty(value = "车速(km/h)")
@Excel(name = "车速(km/h)")
private BigDecimal vehicleSpeed;
/** 累计里程(km) */
@ApiModelProperty(value = "累计里程(km)")
@Excel(name = "累计里程(km)")
private BigDecimal totalMileage;
/** 总电压(V) */
@ApiModelProperty(value = "总电压(V)")
@Excel(name = "总电压(V)")
private BigDecimal totalVoltage;
/** 总电流(A) */
@ApiModelProperty(value = "总电流(A)")
@Excel(name = "总电流(A)")
private BigDecimal totalCurrent;
/** SOC电量百分比(%) */
@ApiModelProperty(value = "SOC电量百分比(%)")
@Excel(name = "SOC电量百分比(%)")
private Integer soc;
/** DC-DC状态 (1:工作 2:断开) */
@ApiModelProperty(value = "DC-DC状态 (1:工作 2:断开)")
@Excel(name = "DC-DC状态 (1:工作 2:断开)")
private Integer dcdcStatus;
/** 档位 (0:空档 1-6:1-6档 13:倒档 14:自动D档 15:停车P档) */
@ApiModelProperty(value = "档位 (0:空档 1-6:1-6档 13:倒档 14:自动D档 15:停车P档)")
@Excel(name = "档位 ")
private Integer gearPosition;
/** 绝缘电阻(kΩ) */
@ApiModelProperty(value = "绝缘电阻(kΩ)")
@Excel(name = "绝缘电阻(kΩ)")
private Integer insulationResistance;
/** 预留字段 */
@ApiModelProperty(value = "预留字段")
@Excel(name = "预留字段")
private String reservedData;
}

View File

@ -0,0 +1,69 @@
package com.evobms.project.vehicledata.domain;
import java.math.BigDecimal;
import java.util.Date;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.evobms.framework.aspectj.lang.annotation.Excel;
import com.evobms.framework.web.domain.BaseEntity;
/**
* 车辆位置数据对象 vehicle_location
*
* @author ruoyi
* @date 2025-11-15
*/
@ApiModel(description = "车辆位置数据对象")
@Data
@EqualsAndHashCode(callSuper = false)
public class VehicleLocation extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ApiModelProperty(value = "主键ID")
private Long id;
/**
* 设备ID
*/
@ApiModelProperty(value = "设备ID")
@Excel(name = "设备ID")
private String deviceId;
/**
* 数据时间戳
*/
@ApiModelProperty(value = "数据时间戳")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "数据时间戳", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date timestamp;
/**
* 定位状态 (0:无效 1:有效)
*/
@ApiModelProperty(value = "定位状态 (0:无效 1:有效)")
@Excel(name = "定位状态 (0:无效 1:有效)")
private Integer positioningStatus;
/**
* 经度
*/
@ApiModelProperty(value = "经度")
@Excel(name = "经度")
private BigDecimal longitude;
/**
* 纬度
*/
@ApiModelProperty(value = "纬度")
@Excel(name = "纬度")
private BigDecimal latitude;
}

View File

@ -0,0 +1,15 @@
package com.evobms.project.vehicledata.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
@ApiModel(value = "ChargeDischargeHistoryDTO", description = "充放电历史统计与明细")
public class ChargeDischargeHistoryDTO {
@ApiModelProperty(value = "月度充放电时长统计")
private List<ChargeDischargeMonthDTO> months;
@ApiModelProperty(value = "区段明细记录")
private List<ChargeDischargeRecordDTO> records;
}

Some files were not shown because too many files have changed in this diff Show More