物联网设备实战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),我也可以提供分层模板和标准实践。需要我整理一份吗?