远程升级
This commit is contained in:
parent
2d072b01ff
commit
b1c02a8e43
@ -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_"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<String, Object> redisTemplate(RedisConnectionFactory factory) {
|
||||
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(factory);
|
||||
|
||||
// 使用Jackson2JsonRedisSerializer序列化值
|
||||
Jackson2JsonRedisSerializer<Object> 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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
@ -45,4 +45,9 @@ public interface CommandType {
|
||||
* 关闭电源
|
||||
*/
|
||||
String POWER_OFF = "POWER_OFF";
|
||||
|
||||
/**
|
||||
* 升级
|
||||
*/
|
||||
String UPGRADE = "UPGRADE";
|
||||
}
|
||||
|
||||
@ -73,4 +73,11 @@ public interface CommandService{
|
||||
* @return 执行结果
|
||||
*/
|
||||
boolean powerOff(EbikeEcuInfo ebikeEcuInfo);
|
||||
|
||||
/**
|
||||
* 远程升级
|
||||
* @param ebikeEcuInfo 中控信息
|
||||
* @return 执行结果
|
||||
*/
|
||||
boolean upgrade(EbikeEcuInfo ebikeEcuInfo,String url);
|
||||
}
|
||||
|
||||
@ -115,7 +115,7 @@ public interface EbikeEcuInfoService extends IService<EbikeEcuInfo> {
|
||||
* @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> {
|
||||
*/
|
||||
EbikeEcuInfo getEcu(String bikeCode);
|
||||
|
||||
/**
|
||||
* 远程升级
|
||||
* @param ebikeEcuInfo 中控信息
|
||||
* @return 结果
|
||||
*/
|
||||
boolean upgrade(EbikeEcuInfo ebikeEcuInfo,String url);
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -126,7 +126,7 @@ public class EbikeEcuInfoServiceImpl extends ServiceImpl<EbikeEcuInfoMapper, Ebi
|
||||
|
||||
@Override
|
||||
public boolean findBike(EbikeEcuInfo ebikeEcuInfo) {
|
||||
return commandService.findBike(ebikeEcuInfo);
|
||||
return commandService.findBike(ebikeEcuInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -161,7 +161,12 @@ public class EbikeEcuInfoServiceImpl extends ServiceImpl<EbikeEcuInfoMapper, Ebi
|
||||
|
||||
@Override
|
||||
public boolean powerOff(EbikeEcuInfo ebikeEcuInfo) {
|
||||
return commandService.powerOff(ebikeEcuInfo);
|
||||
return commandService.powerOff(ebikeEcuInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean upgrade(EbikeEcuInfo ebikeEcuInfo, String url) {
|
||||
return commandService.upgrade(ebikeEcuInfo, url);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -180,7 +185,7 @@ public class EbikeEcuInfoServiceImpl extends ServiceImpl<EbikeEcuInfoMapper, Ebi
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeCommand(String ecuSn, String bikeCode, String commandCode) {
|
||||
public boolean executeCommand(String ecuSn, String bikeCode, String commandCode, String url) {
|
||||
check(ecuSn, bikeCode);
|
||||
EbikeEcuInfo ebikeEcuInfo;
|
||||
if (StringUtil.hasText(ecuSn)) {
|
||||
@ -197,6 +202,7 @@ public class EbikeEcuInfoServiceImpl extends ServiceImpl<EbikeEcuInfoMapper, Ebi
|
||||
case CommandType.UNLOCK -> unLock(ebikeEcuInfo);
|
||||
case CommandType.OPEN_HELMET -> openHelmet(ebikeEcuInfo);
|
||||
case CommandType.POWER_OFF -> powerOff(ebikeEcuInfo);
|
||||
case CommandType.UPGRADE -> upgrade(ebikeEcuInfo, url);
|
||||
default -> throw new EbikeException("该命令不存在/未接入");
|
||||
};
|
||||
}
|
||||
|
||||
@ -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("该命令暂未接入");
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user