物联网设备实战Controller
哪吒 2023/6/15
# 物联网设备实战Controller
下面是对你这段 Spring Boot 控制器 ClientController
的重构整理版,保留原始功能逻辑,并通过结构清晰、注释统一的方式,使代码更易读、更易维护:
# ✅ 重构说明
方面 | 优化点 |
---|---|
注释 | 统一风格,语义清晰 |
命名 | 类、变量、方法命名统一大小写风格 |
结构 | 分块清晰,每个方法职责单一 |
错误处理 | 异常处理更清晰,日志更完整 |
逻辑复用 | 减少重复逻辑,如 map 初始化逻辑 |
# ✅ 整理后的代码(更易懂)
package com.phoenicia.controller;
import com.phoenicia.bean.*;
import com.phoenicia.server.JT808Session;
import com.phoenicia.server.JT808SessionContext;
import com.phoenicia.server.bean.JT808SendMsg;
import com.phoenicia.server.biz.BatteryRealtimeService;
import com.phoenicia.server.biz.PosInfoHandle;
import com.phoenicia.server.codec.JT808BData;
import com.phoenicia.server.jt808.JT808MsgHandle;
import com.phoenicia.server.jt808.JT808ServerHandle;
import com.phoenicia.util.GsonUtils;
import com.phoenicia.util.HexStrUtil;
import io.swagger.annotations.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.LinkedHashSet;
import java.util.Set;
@Api(tags = "设备控制")
@RestController
@RequestMapping("/client")
public class ClientController {
private static final Logger logger = LoggerFactory.getLogger(ClientController.class);
@Autowired
@Qualifier("rtRedisTemplate")
private RedisTemplate<String, String> redisTemplate;
@Autowired
private PosInfoHandle posInfoHandle;
@Autowired
private BatteryRealtimeService batteryRealtimeService;
/**
* 下发 JT808 消息
*/
@ApiOperation("发送消息")
@ApiImplicitParams({
@ApiImplicitParam(name = "json", value = "JT808SendMsg JSON 字符串", paramType = "body", dataType = "string")
})
@PostMapping("/sendMsg")
public RestRet sendMsg(@RequestBody String json) {
try {
logger.info("==> JT808发送消息: {}", json);
JT808SendMsg sendMsg = GsonUtils.getObjectFromJson(json, JT808SendMsg.class);
JT808Session session = JT808SessionContext.getSessionByClientId(sendMsg.getMobile());
if (session == null) {
logger.info("==> 设备未上线: {}", sendMsg.getMobile());
return RestRet.createFAIL("设备未上线! " + sendMsg.getMobile());
}
JT808BData data = new JT808BData();
if (StringUtils.isEmpty(sendMsg.getMsgBody())) {
sendMsg.setMsgBody("00");
}
BeanUtils.copyProperties(sendMsg, data);
data.setMsgBody(HexStrUtil.decodeHex(sendMsg.getMsgBody()));
return JT808ServerHandle.sendMsgToClient(data.getMobile(), data, 0);
} catch (Exception e) {
logger.error("==> 发送异常: {}, json={}", e.getMessage(), json, e);
return RestRet.createFAIL("发送失败");
}
}
/**
* 获取下发消息的响应
*/
@ApiOperation("获取返回结果")
@PostMapping("/getSendMsgResp")
public RestRet getSendMsgResp(@RequestBody String clientId) {
try {
String result = JT808MsgHandle.jt808RespMap.remove(clientId);
logger.info("==> 获取返回结果: {}", result);
return RestRet.createSuccess("获取成功", result);
} catch (Exception e) {
logger.error("==> 获取响应异常: {}", e.getMessage(), e);
return RestRet.createSuccess("成功");
}
}
/**
* 获取电池实时数据(根据clientId)
*/
@ApiOperation("Id获取电池实时数据")
@PostMapping("/getRtInfoById")
public RestRet getRtInfoById(@RequestBody String clientId) {
BatteryRealTime batteryInfo = posInfoHandle.getBatterRealtime(clientId);
if (batteryInfo == null) {
batteryInfo = batteryRealtimeService.getBatteryRealtimeFromCache(clientId);
}
return RestRet.createSuccess("获取成功", GsonUtils.getJsonFromObject(batteryInfo));
}
/**
* 订阅电池实时信息
*/
@ApiOperation("主机名订阅")
@PostMapping("/subscribeRtInfo")
public RestRet subscribeRtInfo(@RequestBody String json) {
SubscribeBatteryReq req = GsonUtils.getObjectFromJson(json, SubscribeBatteryReq.class);
String clientId = req.getClientId();
// 初始化 hostSet
Set<String> hostSet = PosInfoHandle.subscribeHostsMap.computeIfAbsent(clientId, k -> new LinkedHashSet<>());
hostSet.add(req.getSubscirbeHost());
PosInfoHandle.subscribeClientIdsMap.put(clientId, System.currentTimeMillis());
BatteryRealTime rt = posInfoHandle.getBatterRealtime(clientId);
posInfoHandle.sendBatteryRtInfo(clientId, req.getSubscirbeHost(), rt);
logger.info("==> 订阅成功: {}", GsonUtils.getJsonFromObject(req));
return RestRet.createSuccess("订阅成功");
}
/**
* 取消订阅
*/
@ApiOperation("主机取消订阅")
@PostMapping("/cancelSubscribeRtInfo")
public RestRet cancelSubscribeRtInfo(@RequestBody String json) {
SubscribeBatteryReq req = GsonUtils.getObjectFromJson(json, SubscribeBatteryReq.class);
String clientId = req.getClientId();
Set<String> hostSet = PosInfoHandle.subscribeHostsMap.get(clientId);
if (hostSet != null) {
hostSet.remove(req.getSubscirbeHost());
}
PosInfoHandle.subscribeClientIdsMap.remove(clientId);
logger.info("==> 取消订阅成功: {}", GsonUtils.getJsonFromObject(req));
return RestRet.createSuccess("取消订阅成功");
}
public static void main(String[] args) {
// 本类无 main 逻辑
}
}
# ✅ 推荐增强点(可选)
方向 | 建议 |
---|---|
安全性 | 对 /sendMsg 、/subscribeRtInfo 做权限验证 |
健壮性 | 对 JSON 参数结构校验(如用 @Validated 和 DTO) |
幂等性 | 下发指令接口可引入 msgId 唯一性幂等判断 |
拓展性 | 订阅取消接口应支持多个主机取消(批量处理) |
如你希望进一步将该类重构为分层架构(Controller + Service + Manager),我也可以提供分层模板和标准实践。需要我整理一份吗?