智慧充电系统设备管理服务对外接口实现方案

目录

一、前置核心定义

[1. 技术栈](#1. 技术栈)

[2. 核心数据库表(简化版)](#2. 核心数据库表(简化版))

[3. 统一返回结果](#3. 统一返回结果)

[二、接口 1:设备状态查询](#二、接口 1:设备状态查询)

业务说明

[1. DTO(请求参数)](#1. DTO(请求参数))

[2. VO(返回参数)](#2. VO(返回参数))

[3. Controller 接口](#3. Controller 接口)

[4. Service 实现(核心业务逻辑)](#4. Service 实现(核心业务逻辑))

[三、接口 2:远程控枪(核心)](#三、接口 2:远程控枪(核心))

业务说明

[1. 枚举:远程控制指令类型](#1. 枚举:远程控制指令类型)

[2. DTO(请求参数)](#2. DTO(请求参数))

[3. Controller 接口](#3. Controller 接口)

[4. Service 实现(核心业务逻辑)](#4. Service 实现(核心业务逻辑))

[四、接口 3:设备故障查询](#四、接口 3:设备故障查询)

业务说明

[1. DTO(请求参数)](#1. DTO(请求参数))

[2. VO(返回参数)](#2. VO(返回参数))

[3. Controller 接口](#3. Controller 接口)

[4. Service 实现](#4. Service 实现)

五、核心业务总结(三个接口)

六、扩展说明

总结


基于SpringBoot + MyBatis-Plus + Redis + RocketMQ (充电桩行业主流技术栈),实现设备状态查询、远程控枪、设备故障查询 三个核心对外接口,包含完整的接口定义、业务逻辑、代码实现、数据库设计,直接可落地使用。

一、前置核心定义

1. 技术栈

  • 框架:SpringBoot 3.x + Spring MVC
  • ORM:MyBatis-Plus
  • 缓存:Redis(存储设备在线状态、实时数据)
  • 消息队列:RocketMQ(下发远程控制指令)
  • 协议:OCPP 1.6J/2.0.1

2. 核心数据库表(简化版)

复制代码
-- 设备基础表
CREATE TABLE device (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    device_sn VARCHAR(64) NOT NULL COMMENT '设备唯一编号(主键)',
    gun_num VARCHAR(32) NOT NULL COMMENT '枪号',
    imei VARCHAR(64) COMMENT '设备IMEI',
    status TINYINT NOT NULL COMMENT '设备状态 0-离线 1-在线',
    work_status TINYINT NOT NULL COMMENT '工作状态 0-空闲 1-充电中 2-占用 3-故障',
    voltage DECIMAL(10,2) COMMENT '实时电压',
    current DECIMAL(10,2) COMMENT '实时电流',
    temperature DECIMAL(10,2) COMMENT '温度',
    power DECIMAL(10,2) COMMENT '功率',
    create_time DATETIME,
    update_time DATETIME
);

-- 设备故障表
CREATE TABLE device_fault (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    device_sn VARCHAR(64) NOT NULL COMMENT '设备编号',
    gun_num VARCHAR(32) NOT NULL COMMENT '枪号',
    fault_code VARCHAR(32) NOT NULL COMMENT '故障码',
    fault_msg VARCHAR(255) NOT NULL COMMENT '故障描述',
    fault_status TINYINT DEFAULT 0 COMMENT '故障状态 0-待处理 1-已处理',
    create_time DATETIME
);

3. 统一返回结果

复制代码
import lombok.Data;

/**
 * 统一接口返回值
 */
@Data
public class Result<T> {
    private int code;
    private String msg;
    private T data;

    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>();
        result.setCode(200);
        result.setMsg("操作成功");
        result.setData(data);
        return result;
    }

    public static <T> Result<T> fail(String msg) {
        Result<T> result = new Result<>();
        result.setCode(500);
        result.setMsg(msg);
        return result;
    }
}

二、接口 1:设备状态查询

业务说明

  1. 核心业务 :根据设备编号 / 枪号,查询设备在线状态、工作状态、实时电气参数(电压 / 电流 / 温度等);
  2. 数据来源:Redis(实时在线状态) + MySQL(基础信息);
  3. 校验规则:设备必须存在,支持单设备 / 批量查询。

1. DTO(请求参数)

复制代码
import lombok.Data;
import javax.validation.constraints.NotBlank;

/**
 * 设备状态查询请求
 */
@Data
public class DeviceStatusQueryDTO {
    @NotBlank(message = "设备编号不能为空")
    private String deviceSn;
    // 非必填,不填默认查询所有枪
    private String gunNum;
}

2. VO(返回参数)

复制代码
import lombok.Data;
import java.math.BigDecimal;

/**
 * 设备状态返回视图
 */
@Data
public class DeviceStatusVO {
    private String deviceSn;    // 设备编号
    private String gunNum;     // 枪号
    private Integer status;    // 在线状态 0-离线 1-在线
    private Integer workStatus;// 工作状态 0-空闲 1-充电中 2-占用 3-故障
    private BigDecimal voltage;// 电压
    private BigDecimal current;// 电流
    private BigDecimal temperature;// 温度
    private BigDecimal power;  // 功率
    private String statusDesc; // 状态描述
}

3. Controller 接口

复制代码
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;

/**
 * 设备管理接口
 */
@RestController
@RequestMapping("/device/service")
public class DeviceController {

    @Resource
    private DeviceService deviceService;

    /**
     * 1. 设备状态查询
     */
    @PostMapping("/status/query")
    public Result<DeviceStatusVO> queryDeviceStatus(@RequestBody DeviceStatusQueryDTO dto) {
        return Result.success(deviceService.getDeviceStatus(dto));
    }
}

4. Service 实现(核心业务逻辑)

复制代码
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

@Service
public class DeviceServiceImpl implements DeviceService {

    // Redis存储设备在线状态 key: device:online:{deviceSn}
    private static final String DEVICE_ONLINE_KEY = "device:online:%s";
    // 心跳超时时间(30s+10s缓冲)
    private static final long HEARTBEAT_TIMEOUT = 40;

    @Resource
    private DeviceMapper deviceMapper;
    @Resource
    private StringRedisTemplate redisTemplate;

    @Override
    public DeviceStatusVO getDeviceStatus(DeviceStatusQueryDTO dto) {
        // 1. 校验设备是否存在
        Device device = deviceMapper.selectById(dto.getDeviceSn());
        if (device == null) {
            throw new RuntimeException("设备不存在");
        }

        // 2. 从Redis获取实时在线状态(心跳机制:设备30s上报一次)
        String onlineKey = String.format(DEVICE_ONLINE_KEY, dto.getDeviceSn());
        Boolean hasKey = redisTemplate.hasKey(onlineKey);
        // 有key=在线,无key=离线
        device.setStatus(Boolean.TRUE.equals(hasKey) ? 1 : 0);

        // 3. 组装返回数据
        DeviceStatusVO vo = new DeviceStatusVO();
        BeanUtils.copyProperties(device, vo);
        // 填充枪号(不填则用设备默认枪号)
        vo.setGunNum(StringUtils.isBlank(dto.getGunNum()) ? device.getGunNum() : dto.getGunNum());
        // 状态描述
        vo.setStatusDesc(getStatusDesc(device.getStatus(), device.getWorkStatus()));
        return vo;
    }

    /**
     * 状态描述转换
     */
    private String getStatusDesc(Integer status, Integer workStatus) {
        if (status == 0) return "设备离线";
        return switch (workStatus) {
            case 0 -> "空闲";
            case 1 -> "充电中";
            case 2 -> "占用";
            case 3 -> "故障";
            default -> "未知状态";
        };
    }
}

三、接口 2:远程控枪(核心)

业务说明

  1. 核心业务 :远程启动充电 / 停止充电 / 锁枪 / 解锁 / 重启设备(OCPP 协议核心指令);
  2. 执行流程:参数校验 → 设备状态校验 → 消息队列下发指令 → 返回指令下发结果;
  3. 校验规则
    • 设备必须在线才能下发指令;
    • 空闲状态才能启动充电,充电中才能停止充电;
    • 指令类型必须合法。

1. 枚举:远程控制指令类型

复制代码
import lombok.Getter;

/**
 * 远程控枪指令枚举
 */
@Getter
public enum RemoteControlEnum {
    START_CHARGE(1, "启动充电"),
    STOP_CHARGE(2, "停止充电"),
    LOCK_GUN(3, "锁枪"),
    UNLOCK_GUN(4, "解锁枪"),
    REBOOT(5, "设备重启");

    private final int code;
    private final String desc;

    RemoteControlEnum(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }
}

2. DTO(请求参数)

复制代码
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;

/**
 * 远程控枪请求
 */
@Data
public class RemoteControlDTO {
    @NotBlank(message = "设备编号不能为空")
    private String deviceSn;
    @NotBlank(message = "枪号不能为空")
    private String gunNum;
    @NotNull(message = "指令类型不能为空")
    private Integer command; // 对应RemoteControlEnum
    private String operator; // 操作人
}

3. Controller 接口

复制代码
/**
 * 2. 远程控枪接口
 */
@PostMapping("/remote/control")
public Result<String> remoteControlGun(@RequestBody @Valid RemoteControlDTO dto) {
    deviceService.remoteControl(dto);
    return Result.success("指令下发成功,设备正在执行");
}

4. Service 实现(核心业务逻辑)

复制代码
import org.apache.rocketmq.spring.core.RocketMQTemplate;

@Service
public class DeviceServiceImpl implements DeviceService {

    // MQ指令下发主题(设备侧监听该主题执行指令)
    private static final String REMOTE_CONTROL_TOPIC = "ocpp_remote_control_topic";

    @Resource
    private RocketMQTemplate rocketMQTemplate;

    @Override
    public void remoteControl(RemoteControlDTO dto) {
        // 1. 校验设备是否存在
        Device device = deviceMapper.selectById(dto.getDeviceSn());
        if (device == null) {
            throw new RuntimeException("设备不存在");
        }

        // 2. 校验设备在线状态
        String onlineKey = String.format(DEVICE_ONLINE_KEY, dto.getDeviceSn());
        if (!Boolean.TRUE.equals(redisTemplate.hasKey(onlineKey))) {
            throw new RuntimeException("设备离线,无法下发远程指令");
        }

        // 3. 指令合法性校验
        RemoteControlEnum command = null;
        for (RemoteControlEnum e : RemoteControlEnum.values()) {
            if (e.getCode() == dto.getCommand()) {
                command = e;
                break;
            }
        }
        if (command == null) {
            throw new RuntimeException("不支持的指令类型");
        }

        // 4. 业务状态校验(核心)
        validWorkStatus(device, command);

        // 5. 下发指令到MQ(设备侧监听MQ,解析OCPP协议执行)
        rocketMQTemplate.convertAndSend(REMOTE_CONTROL_TOPIC, dto);

        // 6. 日志记录/操作记录(可选)
        log.info("设备{},枪{},下发指令:{}", dto.getDeviceSn(), dto.getGunNum(), command.getDesc());
    }

    /**
     * 工作状态业务校验(核心业务规则)
     */
    private void validWorkStatus(Device device, RemoteControlEnum command) {
        Integer workStatus = device.getWorkStatus();
        switch (command) {
            case START_CHARGE:
                if (workStatus != 0) throw new RuntimeException("设备非空闲,无法启动充电");
                break;
            case STOP_CHARGE:
                if (workStatus != 1) throw new RuntimeException("设备未充电,无法停止");
                break;
            case LOCK_GUN:
            case UNLOCK_GUN:
            case REBOOT:
                // 锁枪/解锁/重启 仅需在线即可,无工作状态限制
                break;
            default:
                throw new RuntimeException("指令校验失败");
        }
    }
}

四、接口 3:设备故障查询

业务说明

  1. 核心业务 :查询设备历史故障、当前未处理故障,支持分页、故障状态筛选;
  2. 业务场景:运维人员查看设备故障,生成工单;
  3. 数据来源:MySQL 设备故障表。

1. DTO(请求参数)

复制代码
import lombok.Data;

/**
 * 设备故障查询请求
 */
@Data
public class DeviceFaultQueryDTO {
    @NotBlank(message = "设备编号不能为空")
    private String deviceSn;
    private String gunNum;
    private Integer faultStatus; // 0-待处理 1-已处理
    private Integer pageNum = 1;
    private Integer pageSize = 10;
}

2. VO(返回参数)

复制代码
import lombok.Data;
import java.time.LocalDateTime;

/**
 * 设备故障返回视图
 */
@Data
public class DeviceFaultVO {
    private String deviceSn;
    private String gunNum;
    private String faultCode;    // 故障码
    private String faultMsg;     // 故障描述
    private Integer faultStatus;// 故障状态
    private String statusDesc;  // 状态描述
    private LocalDateTime createTime; // 故障上报时间
}

3. Controller 接口

复制代码
/**
 * 3. 设备故障查询
 */
@PostMapping("/fault/query")
public Result<Page<DeviceFaultVO>> queryDeviceFault(@RequestBody DeviceFaultQueryDTO dto) {
    return Result.success(deviceService.getDeviceFaultList(dto));
}

4. Service 实现

复制代码
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

@Service
public class DeviceServiceImpl implements DeviceService {

    @Resource
    private DeviceFaultMapper deviceFaultMapper;

    @Override
    public Page<DeviceFaultVO> getDeviceFaultList(DeviceFaultQueryDTO dto) {
        // 1. 分页查询
        Page<DeviceFault> page = new Page<>(dto.getPageNum(), dto.getPageSize());
        LambdaQueryWrapper<DeviceFault> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(DeviceFault::getDeviceSn, dto.getDeviceSn());
        // 枪号筛选
        if (StringUtils.isNotBlank(dto.getGunNum())) {
            wrapper.eq(DeviceFault::getGunNum, dto.getGunNum());
        }
        // 故障状态筛选
        if (dto.getFaultStatus() != null) {
            wrapper.eq(DeviceFault::getFaultStatus, dto.getFaultStatus());
        }
        // 按故障时间倒序
        wrapper.orderByDesc(DeviceFault::getCreateTime);

        IPage<DeviceFault> faultPage = deviceFaultMapper.selectPage(page, wrapper);

        // 2. 转换VO
        Page<DeviceFaultVO> resultPage = new Page<>();
        BeanUtils.copyProperties(faultPage, resultPage);
        List<DeviceFaultVO> voList = faultPage.getRecords().stream().map(fault -> {
            DeviceFaultVO vo = new DeviceFaultVO();
            BeanUtils.copyProperties(fault, vo);
            vo.setStatusDesc(fault.getFaultStatus() == 0 ? "待处理" : "已处理");
            return vo;
        }).collect(Collectors.toList());
        resultPage.setRecords(voList);
        return resultPage;
    }
}

五、核心业务总结(三个接口)

接口 核心业务逻辑 技术要点
设备状态查询 1. 校验设备存在2. Redis 校验在线状态(心跳机制)3. 拼接实时状态返回 Redis+MySQL 双数据源
远程控枪 1. 设备在线校验2. 指令合法性校验3. 工作状态业务校验4. MQ 下发 OCPP 指令 状态机校验 + 消息队列异步指令
设备故障查询 1. 多条件筛选故障2. 分页查询3. 状态描述转换 分页查询 + 数据转换

六、扩展说明

  1. 协议适配 :设备侧监听 MQ 后,会将指令转换为OCPP 1.6J/2.0.1协议报文发送给充电桩硬件;
  2. 心跳机制:设备每 30s 上报心跳,Redis 缓存心跳 key,超时 40s 判定为离线;
  3. 高可用 :接口添加熔断、限流、日志埋点,适配充电桩高并发场景;
  4. 安全 :生产环境需添加接口鉴权(Token)、操作日志、敏感指令二次确认

总结

  1. 三个接口完全匹配设备管理服务 的对外接口需求,覆盖状态监控、远程控制、运维故障三大核心场景;
  2. 代码遵循业务校验优先、数据分层、异步解耦的充电桩行业最佳实践;
  3. 可直接集成到充电系统中,搭配 OCPP 协议解析模块即可对接物理充电桩。
相关推荐
xiaoye37082 小时前
Spring 事务传播机制 + 隔离级别
java·后端·spring
gQ85v10Db2 小时前
Redis分布式锁进阶第十七篇:微服务分布式锁全局治理 + 跨团队统一规范落地 + 全链路稳定性提升方案
redis·分布式·微服务
Arya_aa2 小时前
数据字典模块–JSR303参数校验
java
明月(Alioo)3 小时前
给 AI Agent 装上“大脑“:Java语言中Code Interpreter 的设计与实现
java·ai·agent
QuZero3 小时前
StampedLock Mechanism
java·算法
Javatutouhouduan3 小时前
Java小白如何快速玩转Redis?
java·数据库·redis·分布式锁·java面试·后端开发·java程序员
xuhaoyu_cpp_java3 小时前
Spring学习(一)
java·经验分享·笔记·学习·spring
kybs19913 小时前
springboot视频推荐系统--附源码72953
java·spring boot·python·eclipse·asp.net·php·idea
无限进步_4 小时前
C++ 多态机制完全解析:从虚函数重写到动态绑定原理
java·c语言·jvm·数据结构·c++·windows·后端