From b1c02a8e435580b8e35d4d6249825a453689af788974d434e72b3ff2312c32a1 Mon Sep 17 00:00:00 2001 From: attiya <2413103649@qq.com> Date: Mon, 17 Nov 2025 16:00:56 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=9C=E7=A8=8B=E5=8D=87=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cdzy/common/utils/RemoteFileUtils.java | 110 ++++++++++++++++++ .../cdzy/operations/config/RedisConfig.java | 39 +++++++ .../controller/EbikeEcuInfoController.java | 16 ++- .../EbikeRefundReviewController.java | 1 + .../cdzy/operations/enums/CommandType.java | 5 + .../operations/service/CommandService.java | 7 ++ .../service/EbikeEcuInfoService.java | 9 +- .../service/impl/CommandServiceImpl.java | 52 ++++++--- .../service/impl/EbikeEcuInfoServiceImpl.java | 12 +- .../cdzy/operations/utils/CommandUtil.java | 15 ++- .../EbikeOperationsApplicationTests.java | 8 +- 11 files changed, 244 insertions(+), 30 deletions(-) create mode 100644 ebike-common/src/main/java/com/cdzy/common/utils/RemoteFileUtils.java create mode 100644 ebike-operations/src/main/java/com/cdzy/operations/config/RedisConfig.java diff --git a/ebike-common/src/main/java/com/cdzy/common/utils/RemoteFileUtils.java b/ebike-common/src/main/java/com/cdzy/common/utils/RemoteFileUtils.java new file mode 100644 index 0000000..0a80b43 --- /dev/null +++ b/ebike-common/src/main/java/com/cdzy/common/utils/RemoteFileUtils.java @@ -0,0 +1,110 @@ +package com.cdzy.common.utils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; + +public class RemoteFileUtils { + + /** + * 获取远程文件并返回File对象 + */ + public static File getRemoteFile(String fileUrl) throws IOException { + return getRemoteFile(fileUrl, null); + } + + /** + * 获取远程文件并保存到指定路径 + */ + public static File getRemoteFile(String fileUrl, String localPath) throws IOException { + validateUrl(fileUrl); + + URL url = new URL(fileUrl); + File outputFile; + + if (localPath != null && !localPath.trim().isEmpty()) { + outputFile = new File(localPath); + // 确保目录存在 + outputFile.getParentFile().mkdirs(); + } else { + // 使用临时文件 + String extension = getFileExtension(fileUrl); + String prefix = "remote_file_" + System.currentTimeMillis() + "_"; + String suffix = extension != null ? "." + extension : ".tmp"; + Path tempPath = Files.createTempFile(prefix, suffix); + outputFile = tempPath.toFile(); + + // 程序退出时删除临时文件 + outputFile.deleteOnExit(); + } + + // 下载文件 + try (InputStream in = url.openStream(); + FileOutputStream out = new FileOutputStream(outputFile)) { + + byte[] buffer = new byte[8192]; + int bytesRead; + long totalBytes = 0; + + while ((bytesRead = in.read(buffer)) != -1) { + out.write(buffer, 0, bytesRead); + totalBytes += bytesRead; + } + } + + return outputFile; + } + + /** + * 获取文件扩展名 + */ + private static String getFileExtension(String url) { + if (url == null || url.isEmpty()) { + return null; + } + + int lastDotIndex = url.lastIndexOf('.'); + int lastSlashIndex = Math.max(url.lastIndexOf('/'), url.lastIndexOf('\\')); + + if (lastDotIndex > lastSlashIndex && lastDotIndex < url.length() - 1) { + String extension = url.substring(lastDotIndex + 1); + // 移除可能的查询参数 + int paramIndex = extension.indexOf('?'); + if (paramIndex != -1) { + extension = extension.substring(0, paramIndex); + } + return extension; + } + + return null; + } + + /** + * 验证URL格式 + */ + private static void validateUrl(String url) { + if (url == null || url.trim().isEmpty()) { + throw new IllegalArgumentException("URL cannot be null or empty"); + } + + if (!url.startsWith("http://") && !url.startsWith("https://")) { + throw new IllegalArgumentException("URL must start with http:// or https://"); + } + } + + /** + * 获取文件信息 + */ + public static void printFileInfo(File file) { + if (file != null && file.exists()) { + System.out.println("File name: " + file.getName()); + System.out.println("File path: " + file.getAbsolutePath()); + System.out.println("File size: " + file.length() + " bytes"); + System.out.println("Is temporary: " + file.getName().startsWith("remote_file_")); + } + } +} \ No newline at end of file diff --git a/ebike-operations/src/main/java/com/cdzy/operations/config/RedisConfig.java b/ebike-operations/src/main/java/com/cdzy/operations/config/RedisConfig.java new file mode 100644 index 0000000..105df32 --- /dev/null +++ b/ebike-operations/src/main/java/com/cdzy/operations/config/RedisConfig.java @@ -0,0 +1,39 @@ +package com.cdzy.operations.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + + +/** + * @author attiya + * @since 2025-03-20 + */ +@Configuration +public class RedisConfig { + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory factory) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(factory); + + // 使用Jackson2JsonRedisSerializer序列化值 + Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(Object.class); + ObjectMapper mapper = new ObjectMapper(); + mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL); + serializer.setObjectMapper(mapper); + + // 设置键和值的序列化器 + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(serializer); + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(serializer); + + template.afterPropertiesSet(); + return template; + } +} diff --git a/ebike-operations/src/main/java/com/cdzy/operations/controller/EbikeEcuInfoController.java b/ebike-operations/src/main/java/com/cdzy/operations/controller/EbikeEcuInfoController.java index b4bdeb8..c0da0a0 100644 --- a/ebike-operations/src/main/java/com/cdzy/operations/controller/EbikeEcuInfoController.java +++ b/ebike-operations/src/main/java/com/cdzy/operations/controller/EbikeEcuInfoController.java @@ -2,6 +2,7 @@ package com.cdzy.operations.controller; import com.cdzy.common.model.request.PageParam; import com.cdzy.common.model.response.JsonResult; +import com.cdzy.operations.enums.CommandType; import com.cdzy.operations.model.dto.EbikeEcuInOverview; import com.cdzy.operations.model.entity.EbikeEcuInfo; import com.cdzy.operations.model.vo.EbikeEcuInfoBatchVo; @@ -158,8 +159,19 @@ public class EbikeEcuInfoController { */ @GetMapping("executeCommand") public JsonResult executeCommand(String ecuSn,String bikeCode,@NotNull(message = "命令编码不能为空") String commandCode) { - boolean findBike = ebikeEcuInfoService.executeCommand(ecuSn,bikeCode,commandCode); - return JsonResult.success(findBike); + boolean result = ebikeEcuInfoService.executeCommand(ecuSn,bikeCode,commandCode,null); + return JsonResult.success(result); + } + + /** + * 远程升级 + * + * @return 执行结构 + */ + @GetMapping("upgrade") + public JsonResult upgrade(String ecuSn,String bikeCode,@NotNull(message = "远程文件地址不能为空") String url) { + boolean result = ebikeEcuInfoService.executeCommand(ecuSn,bikeCode, CommandType.UPGRADE,url); + return JsonResult.success(result); } diff --git a/ebike-operations/src/main/java/com/cdzy/operations/controller/EbikeRefundReviewController.java b/ebike-operations/src/main/java/com/cdzy/operations/controller/EbikeRefundReviewController.java index d8df83d..49a036f 100644 --- a/ebike-operations/src/main/java/com/cdzy/operations/controller/EbikeRefundReviewController.java +++ b/ebike-operations/src/main/java/com/cdzy/operations/controller/EbikeRefundReviewController.java @@ -12,6 +12,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** + * 退款审核 控制层。 * @author yanglei * @since 2025-11-14 16:58 */ diff --git a/ebike-operations/src/main/java/com/cdzy/operations/enums/CommandType.java b/ebike-operations/src/main/java/com/cdzy/operations/enums/CommandType.java index 633d54e..8843451 100644 --- a/ebike-operations/src/main/java/com/cdzy/operations/enums/CommandType.java +++ b/ebike-operations/src/main/java/com/cdzy/operations/enums/CommandType.java @@ -45,4 +45,9 @@ public interface CommandType { * 关闭电源 */ String POWER_OFF = "POWER_OFF"; + + /** + * 升级 + */ + String UPGRADE = "UPGRADE"; } diff --git a/ebike-operations/src/main/java/com/cdzy/operations/service/CommandService.java b/ebike-operations/src/main/java/com/cdzy/operations/service/CommandService.java index b222dfc..e0182ef 100644 --- a/ebike-operations/src/main/java/com/cdzy/operations/service/CommandService.java +++ b/ebike-operations/src/main/java/com/cdzy/operations/service/CommandService.java @@ -73,4 +73,11 @@ public interface CommandService{ * @return 执行结果 */ boolean powerOff(EbikeEcuInfo ebikeEcuInfo); + + /** + * 远程升级 + * @param ebikeEcuInfo 中控信息 + * @return 执行结果 + */ + boolean upgrade(EbikeEcuInfo ebikeEcuInfo,String url); } diff --git a/ebike-operations/src/main/java/com/cdzy/operations/service/EbikeEcuInfoService.java b/ebike-operations/src/main/java/com/cdzy/operations/service/EbikeEcuInfoService.java index 66c362a..a830da8 100644 --- a/ebike-operations/src/main/java/com/cdzy/operations/service/EbikeEcuInfoService.java +++ b/ebike-operations/src/main/java/com/cdzy/operations/service/EbikeEcuInfoService.java @@ -115,7 +115,7 @@ public interface EbikeEcuInfoService extends IService { * @param commandCode 命令编码 * @return 执行结果 */ - boolean executeCommand(String ecuSn, String bikeCode, String commandCode); + boolean executeCommand(String ecuSn, String bikeCode, String commandCode,String url); /** * 根据车辆编号获取中控信息 @@ -124,4 +124,11 @@ public interface EbikeEcuInfoService extends IService { */ EbikeEcuInfo getEcu(String bikeCode); + /** + * 远程升级 + * @param ebikeEcuInfo 中控信息 + * @return 结果 + */ + boolean upgrade(EbikeEcuInfo ebikeEcuInfo,String url); + } diff --git a/ebike-operations/src/main/java/com/cdzy/operations/service/impl/CommandServiceImpl.java b/ebike-operations/src/main/java/com/cdzy/operations/service/impl/CommandServiceImpl.java index 26fa959..1853d78 100644 --- a/ebike-operations/src/main/java/com/cdzy/operations/service/impl/CommandServiceImpl.java +++ b/ebike-operations/src/main/java/com/cdzy/operations/service/impl/CommandServiceImpl.java @@ -7,7 +7,6 @@ import com.cdzy.operations.enums.EcuBrand; import com.cdzy.operations.model.entity.EbikeEcuInfo; import com.cdzy.operations.service.CommandService; import com.cdzy.operations.utils.CommandUtil; -import com.fasterxml.jackson.core.JsonProcessingException; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -41,8 +40,8 @@ public class CommandServiceImpl implements CommandService { case EcuBrand.GUANG_HE_TONG: String command; try { - command = CommandUtil.guang_he_tong(ebikeEcuInfo.getEcuSn(), taskId, CommandType.FIND_BIKE); - } catch (JsonProcessingException e) { + command = CommandUtil.guang_he_tong(ebikeEcuInfo.getEcuSn(), taskId, CommandType.FIND_BIKE,null); + } catch (Exception e) { throw new RuntimeException(e); } return submitTaskAndWait(topic, taskId, command); @@ -58,8 +57,8 @@ public class CommandServiceImpl implements CommandService { case EcuBrand.GUANG_HE_TONG: String command; try { - command = CommandUtil.guang_he_tong(ebikeEcuInfo.getEcuSn(), taskId, CommandType.GPS); - } catch (JsonProcessingException e) { + command = CommandUtil.guang_he_tong(ebikeEcuInfo.getEcuSn(), taskId, CommandType.GPS,null); + } catch (Exception e) { throw new RuntimeException(e); } return submitTaskAndWait(topic, taskId, command); @@ -75,8 +74,8 @@ public class CommandServiceImpl implements CommandService { case EcuBrand.GUANG_HE_TONG: String command; try { - command = CommandUtil.guang_he_tong(ebikeEcuInfo.getEcuSn(), taskId, CommandType.OPEN_BATTERY_LOCK); - } catch (JsonProcessingException e) { + command = CommandUtil.guang_he_tong(ebikeEcuInfo.getEcuSn(), taskId, CommandType.OPEN_BATTERY_LOCK,null); + } catch (Exception e) { throw new RuntimeException(e); } return submitTaskAndWait(topic, taskId, command); @@ -92,8 +91,8 @@ public class CommandServiceImpl implements CommandService { case EcuBrand.GUANG_HE_TONG: String command; try { - command = CommandUtil.guang_he_tong(ebikeEcuInfo.getEcuSn(), taskId, CommandType.CLOSE_BATTERY_LOCK); - } catch (JsonProcessingException e) { + command = CommandUtil.guang_he_tong(ebikeEcuInfo.getEcuSn(), taskId, CommandType.CLOSE_BATTERY_LOCK,null); + } catch (Exception e) { throw new RuntimeException(e); } return submitTaskAndWait(topic, taskId, command); @@ -109,8 +108,8 @@ public class CommandServiceImpl implements CommandService { case EcuBrand.GUANG_HE_TONG: String command; try { - command = CommandUtil.guang_he_tong(ebikeEcuInfo.getEcuSn(), taskId, CommandType.LOCK); - } catch (JsonProcessingException e) { + command = CommandUtil.guang_he_tong(ebikeEcuInfo.getEcuSn(), taskId, CommandType.LOCK,null); + } catch (Exception e) { throw new RuntimeException(e); } return submitTaskAndWait(topic, taskId, command); @@ -126,8 +125,8 @@ public class CommandServiceImpl implements CommandService { case EcuBrand.GUANG_HE_TONG: String command; try { - command = CommandUtil.guang_he_tong(ebikeEcuInfo.getEcuSn(), taskId, CommandType.UNLOCK); - } catch (JsonProcessingException e) { + command = CommandUtil.guang_he_tong(ebikeEcuInfo.getEcuSn(), taskId, CommandType.UNLOCK,null); + } catch (Exception e) { throw new RuntimeException(e); } return submitTaskAndWait(topic, taskId, command); @@ -148,8 +147,8 @@ public class CommandServiceImpl implements CommandService { case EcuBrand.GUANG_HE_TONG: String command; try { - command = CommandUtil.guang_he_tong(ebikeEcuInfo.getEcuSn(), taskId, CommandType.OPEN_HELMET); - } catch (JsonProcessingException e) { + command = CommandUtil.guang_he_tong(ebikeEcuInfo.getEcuSn(), taskId, CommandType.OPEN_HELMET,null); + } catch (Exception e) { throw new RuntimeException(e); } return submitTaskAndWait(topic, taskId, command); @@ -165,8 +164,25 @@ public class CommandServiceImpl implements CommandService { case EcuBrand.GUANG_HE_TONG: String command; try { - command = CommandUtil.guang_he_tong(ebikeEcuInfo.getEcuSn(), taskId, CommandType.POWER_OFF); - } catch (JsonProcessingException e) { + command = CommandUtil.guang_he_tong(ebikeEcuInfo.getEcuSn(), taskId, CommandType.POWER_OFF,null); + } catch (Exception e) { + throw new RuntimeException(e); + } + return submitTaskAndWait(topic, taskId, command); + default: + throw new EbikeException("该品牌中控暂未接入"); + } + } + + @Override + public boolean upgrade(EbikeEcuInfo ebikeEcuInfo,String url) { + String taskId = createTaskId(); + switch (ebikeEcuInfo.getEcuBrand()) { + case EcuBrand.GUANG_HE_TONG: + String command; + try { + command = CommandUtil.guang_he_tong(ebikeEcuInfo.getEcuSn(), taskId, CommandType.UPGRADE,url); + } catch (Exception e) { throw new RuntimeException(e); } return submitTaskAndWait(topic, taskId, command); @@ -192,7 +208,7 @@ public class CommandServiceImpl implements CommandService { sendMessageToQueue(topic, taskData); // 等待结果,5秒超时 - return future.orTimeout(5, TimeUnit.SECONDS).join(); + return future.orTimeout(10, TimeUnit.SECONDS).join(); } catch (Exception e) { pendingRequests.remove(taskId); diff --git a/ebike-operations/src/main/java/com/cdzy/operations/service/impl/EbikeEcuInfoServiceImpl.java b/ebike-operations/src/main/java/com/cdzy/operations/service/impl/EbikeEcuInfoServiceImpl.java index bfe2a38..d56d6ff 100644 --- a/ebike-operations/src/main/java/com/cdzy/operations/service/impl/EbikeEcuInfoServiceImpl.java +++ b/ebike-operations/src/main/java/com/cdzy/operations/service/impl/EbikeEcuInfoServiceImpl.java @@ -126,7 +126,7 @@ public class EbikeEcuInfoServiceImpl extends ServiceImpl unLock(ebikeEcuInfo); case CommandType.OPEN_HELMET -> openHelmet(ebikeEcuInfo); case CommandType.POWER_OFF -> powerOff(ebikeEcuInfo); + case CommandType.UPGRADE -> upgrade(ebikeEcuInfo, url); default -> throw new EbikeException("该命令不存在/未接入"); }; } diff --git a/ebike-operations/src/main/java/com/cdzy/operations/utils/CommandUtil.java b/ebike-operations/src/main/java/com/cdzy/operations/utils/CommandUtil.java index beb8615..f24a54e 100644 --- a/ebike-operations/src/main/java/com/cdzy/operations/utils/CommandUtil.java +++ b/ebike-operations/src/main/java/com/cdzy/operations/utils/CommandUtil.java @@ -1,11 +1,15 @@ package com.cdzy.operations.utils; +import cn.hutool.core.io.FileUtil; import com.cdzy.common.ex.EbikeException; +import com.cdzy.common.utils.RemoteFileUtils; import com.cdzy.operations.enums.CommandType; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import java.io.File; +import java.io.IOException; + /** * @author attiya * @since 2025-10-30 @@ -16,7 +20,7 @@ public class CommandUtil { private static final String prefix = "ecu/cmd/cdzybms/"; - public static String guang_he_tong(String ecuSn, String taskId, String code) throws JsonProcessingException { + public static String guang_he_tong(String ecuSn, String taskId, String code,String url) throws IOException { String topic = prefix + ecuSn; ObjectNode jsonNode = mapper.createObjectNode(); jsonNode.put("topic", topic); @@ -63,6 +67,13 @@ public class CommandUtil { ObjectNode objectNode_100 = mapper.readValue(command_100, ObjectNode.class); jsonNode.put("command", objectNode_100.toString()); break; + case CommandType.UPGRADE: + File tempFile = RemoteFileUtils.getRemoteFile(url); + long size = FileUtil.size(tempFile); + String command_35 = "{\"tid\":\""+taskId+"\",\"c\":35,\"param\":{\"len\":"+size+",\"url\":\""+url+"\"}}"; + ObjectNode objectNode_35 = mapper.readValue(command_35, ObjectNode.class); + jsonNode.put("command", objectNode_35.toString()); + break; default: throw new EbikeException("该命令暂未接入"); } diff --git a/ebike-operations/src/test/java/com/cdzy/operations/EbikeOperationsApplicationTests.java b/ebike-operations/src/test/java/com/cdzy/operations/EbikeOperationsApplicationTests.java index a7fc0ec..3ba13e0 100644 --- a/ebike-operations/src/test/java/com/cdzy/operations/EbikeOperationsApplicationTests.java +++ b/ebike-operations/src/test/java/com/cdzy/operations/EbikeOperationsApplicationTests.java @@ -1,17 +1,17 @@ package com.cdzy.operations; -import com.cdzy.operations.utils.CommandUtil; -import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import java.io.IOException; + @SpringBootTest class EbikeOperationsApplicationTests { @Test - void contextLoads() throws JsonProcessingException { - System.out.println(CommandUtil.guang_he_tong("2370171956","2370171956","LOCK")); + void contextLoads() throws IOException { + System.out.println("http://47.109.141.125:9000/operations-objects/test/gps_bass.bin".getBytes().length); } }