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

目录

一、前置核心定义

[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 协议解析模块即可对接物理充电桩。
相关推荐
fengxin_rou11 分钟前
深入理解Java类加载机制:从原理到实战详解
java·开发语言
糖果店的幽灵13 分钟前
Spring AI 从入门到精通-Prompt 工程
java·spring·prompt
小江的记录本15 分钟前
【Spring全家桶】Spring Cloud 2023.0.x:配置中心:Nacos Config、Apollo(附《思维导图》+《面试高频考点清单》)
java·spring boot·后端·python·spring·spring cloud·面试
weixin_4083180417 分钟前
2026年医疗直播行业趋势报告:技术方向、监管变化与市场格局
java·大数据·人工智能
linge_sun17 分钟前
SpringAI 五步提示词大法:构建高效 AI 提示词
java·人工智能·ai编程
Demon1_Coder19 分钟前
Day4-微服务-Seata
微服务·云原生·架构
huipeng92622 分钟前
企业级微服务开发实战(三):公共模块设计与统一规范封装
java·spring boot·spring cloud·微服务·架构·系统架构·php
我登哥MVP28 分钟前
Spring Boot 从“会用”到“精通”:参数绑定体系全景
java·spring boot·spring·servlet·maven·intellij-idea·mybatis
C137的本贾尼30 分钟前
JDBC 编程:用 Java 连接 MySQL
java·开发语言·mysql
华大哥37 分钟前
spring boot 和php 调用 LibreOffice 转换 Excel 到 PDF 完整实现
java·pdf·excel