17 KiB
17 KiB
MQTT 智能充电机器人系统 - 开发方案
1. 引言
本文档旨在为 "MQTT 智能充电机器人系统" 提供详细的开发方案。该方案基于已确认的需求文档 (requirements.md) 和技术选型,旨在指导后端平台的开发工作。项目目标是构建一个稳定、可扩展的后端服务,用于管理用户、调度充电机器人、处理充电业务逻辑,并通过 MQTT 与机器人硬件进行通信。
2. 系统架构概览
系统采用典型的 前后端分离 + 物联网 (IoT) 架构:
- 后端平台 (本项目核心): 基于 Spring Boot,负责业务逻辑处理、数据持久化、用户认证授权、与 MQTT Broker 交互。
- MQTT Broker: 外部公共服务(如 EMQX),作为消息中间件,负责平台与机器人之间的消息传递。需要用户提供具体的连接信息和认证凭据。
- 充电机器人 (硬件): 基于 ESP32-CAM,负责执行指令、采集状态、通过 MQTT 与 Broker 通信。本项目不涉及硬件的具体开发。
- 前端应用 (可选): Web 或 App,通过调用后端 API 与系统交互。本项目不包含前端开发。
关键技术栈:
- 后端: Spring Boot 2.7.0, Java 1.8
- 数据访问: MyBatis-Plus, MySQL
- 缓存: Redis (用于 Session 管理等)
- 消息队列: MQTT (使用 Eclipse Paho Java Client)
- API 文档: Knife4j
3. 开发阶段与模块划分
我们将开发过程划分为以下几个主要阶段/模块:
阶段一:基础架构与用户管理
- 环境配置: 确认 JDK, Maven, MySQL, Redis 环境。
- 数据库初始化:
- 根据
requirements.md中的设计,创建数据库mqtt_charging_system(或使用现有my_db并调整)。 - 编写并执行 SQL DDL 脚本,创建
user,charging_robot,parking_spot,charging_session,activation_code表(详细见第 4 节)。
- 根据
- 用户模块实现:
- 创建
User实体类 (model/entity/User.java),包含余额、角色等字段。 - 创建
UserMapper接口 (mapper/UserMapper.java)。 - 创建
UserService接口及实现 (service/UserService.java,service/impl/UserServiceImpl.java),包含用户注册、查询、余额更新等方法。 - 实现密码存储:使用 Spring Security 的
BCryptPasswordEncoder对密码进行加密。在config包下配置PasswordEncoderBean。 - 创建
UserController(controller/UserController.java),提供登录接口。
- 创建
- 认证与授权:
- 实现登录接口
/api/user/login。 - 登录成功后,使用 Spring Session (已配置为 Redis 存储) 存储用户会话信息。
- 后续请求通过 Session 进行用户身份验证。
- 实现简单的基于角色的访问控制:在需要权限的 Controller 方法或 Service 方法上,通过检查当前登录用户的
role字段进行判断。可以创建一个简单的 AOP 切面或直接在方法内判断。
- 实现登录接口
阶段二:MQTT 集成
- 配置 MQTT 连接:
- 在
application.yml中添加 MQTT Broker 的url,username,password, 以及用于命令和状态的commandTopic,statusTopic基础路径。这些值需要用户提供。 - 创建
MqttProperties.java(config/properties/MqttProperties.java) 类,使用@ConfigurationProperties读取配置。
- 在
- 实现 MQTT 客户端:
- 创建
MqttConfig.java(config/MqttConfig.java)。 - 配置
MqttConnectOptionsBean,设置用户名、密码、自动重连、清理会话等。 - 配置
MqttClientBean,并在应用启动后连接到 Broker。 - 重点: 实现
MqttCallbackExtended接口,处理连接成功 (connectComplete)、连接丢失 (connectionLost)、消息到达 (messageArrived) 事件。 - 在
connectComplete中,订阅状态 Topic (robot/status/+)。+是通配符,用于接收所有机器人的状态。 - 在
connectionLost中,记录日志并依赖 Paho 客户端的自动重连机制。 - 在
messageArrived中,将接收到的消息(JSON 字符串)传递给专门的消息处理服务。
- 创建
- 实现消息发布与处理:
- 创建
RobotTaskService.java(service/RobotTaskService.java) 用于管理robot_task表的 CRUD 和状态检查。 - 创建
MqttService.java(service/MqttService.java)。 - 提供
sendCommand(String robotId, String commandType, String payloadJson, Long sessionId)方法:- 调用
RobotTaskService检查robotId是否有状态为SENT的任务。如果有,则不允许发送,直接返回错误或特定状态。 - 如果允许发送,调用
RobotTaskService在robot_task表创建PENDING状态的任务记录。 - 构造实际的 MQTT Topic (
robot/command/{robotId}) - 调用 Paho 客户端的
publish方法发送 MQTT 消息。 - 发送成功后,立即调用
RobotTaskService将对应任务状态更新为SENT并记录sent_time。
- 调用
- 创建
MqttMessageHandler.java(service/MqttMessageHandler.java或类似名称)。 - 提供
handleStatusUpdate(String topic, String payload)方法,由MqttCallback的messageArrived调用。 - 在此方法中:
- 解析
topic获取robotId。 - 使用 Gson (或 Jackson) 将
payload(JSON) 解析为RobotStatusDTO。 - 根据收到的状态,调用
RobotTaskService查找并更新对应的SENT任务状态为ACKNOWLEDGED_SUCCESS或ACKNOWLEDGED_FAILURE,记录ack_time。 - 在任务状态更新成功后,再根据 DTO 中的
status字段,调用相应的业务逻辑(如ChargingService更新机器人状态、处理充电完成事件等)。
- 解析
- 创建
阶段三:核心充电业务逻辑
- 机器人与车位管理:
- 创建
ChargingRobot和ParkingSpot实体、Mapper、Service、Controller。 - 提供基础的 CRUD 接口供管理员使用(可选,根据
requirements.md)。 ChargingRobotService需要包含更新机器人状态(如idle,moving,charging,error)和位置的方法。
- 创建
- 充电会话管理:
- 创建
ChargingSession实体、Mapper、Service。
- 创建
- 充电流程实现:
- 创建
ChargingController.java(controller/ChargingController.java)。 - 实现
/api/charging/request接口:- 接收用户请求(包含
spotId)。 - 验证用户登录状态和余额。
- 调用
ChargingRobotService查找可用机器人。 - 调用
MqttService.sendCommand发送move_to_spot指令 (该方法内部会检查任务表并创建任务记录)。如果返回错误(机器人忙),则告知用户。 - (可选)创建或更新
ChargingSession记录为pending或robot_moving状态。
- 接收用户请求(包含
- 在
MqttMessageHandler中处理状态更新:- 首先更新
robot_task表状态。 arrived_at_spot(确认move_to_spot成功后): 更新机器人DB状态,调用MqttService.sendCommand发送start_charge指令。charging(确认start_charge可能隐含的响应,或只是状态更新): 记录开始时间,更新ChargingSession。charge_complete(确认充电结束,可能是主动上报或响应stop_charge): 记录结束时间、时长,触发计费,更新用户余额,更新ChargingSession。error: 记录错误,更新机器人和ChargingSession状态。
- 首先更新
- 实现
/api/charging/stop接口(用户手动停止):- 调用
MqttService.sendCommand发送stop_charge指令。 - 后续处理逻辑依赖于
charge_complete或error消息的处理。
- 调用
- 创建
- 计费逻辑:
- 在
UserService或单独的BillingService中实现计费方法。 - 根据
ChargingSession的总时长和预设的单价计算费用。 - 调用
UserService更新用户余额 (注意并发安全,可使用数据库行锁或乐观锁)。
- 在
阶段四:激活码与完善
- 激活码模块:
- 创建
ActivationCode实体、Mapper、Service。 - 实现激活码生成逻辑(管理员功能,可在 Service 中提供)。
- 创建
ActivationCodeController.java(controller/ActivationCodeController.java)。 - 实现
/api/codes/redeem接口:接收激活码,验证有效性,调用UserService更新用户余额,将激活码标记为已使用。
- 创建
- API 文档:
- 在所有 Controller 和 DTO 上添加 Knife4j (Swagger) 注解 (
@Api,@ApiOperation,@ApiModelProperty等)。
- 在所有 Controller 和 DTO 上添加 Knife4j (Swagger) 注解 (
- 测试:
- 编写单元测试 (JUnit) 覆盖 Service 层核心逻辑。
- 进行接口测试(使用 Postman 或类似工具)。
- 难点: MQTT 交互的测试,可能需要 Mock
MqttClient或搭建本地 MQTT Broker 进行集成测试。
- 错误处理与日志:
- 完善全局异常处理 (
GlobalExceptionHandler.java)。 - 在关键业务点添加详细日志(使用 SLF4j)。
- 新增:实现任务超时处理逻辑。
- 创建
TaskTimeoutHandler.java(service/TaskTimeoutHandler.java或类似名称)。 - 使用
@Scheduled注解创建一个定时任务,定期执行。 - 在定时任务中,调用
RobotTaskService查找状态为SENT且超时的任务。 - 对于超时的任务,更新其状态为
TIMED_OUT,记录错误信息。 - 根据业务需要,可能需要同步更新关联的
charging_robot和charging_session的状态。
- 创建
- 完善全局异常处理 (
阶段 3: 管理端核心功能完善 与 前后端数据对接 (进行中)
目标: 搭建核心管理页面框架,并将部分用户端和管理端页面与真实后端数据对接。
进行中 / 即将进行:
-
用户仪表盘 (
/dashboard) 数据对接 (规划中)- 目的: 将用户仪表盘的模拟数据替换为真实的后端数据,包括"当前充电状态"和"用户统计数据"(本月充电次数、本月总消费)。
- 后端接口需求:
GET /api/session/my/active: 获取用户当前激活的充电会话。若无激活会话,返回 null 或明确的空状态。- Controller:
ChargingSessionController - Service: 查询用户状态为进行中(如
CHARGING_IN_PROGRESS)的会话。
- Controller:
GET /api/user/stats/mine: 获取当前登录用户的统计数据。- 响应示例:
{ "monthlyCharges": 15, "monthlySpending": 250.75 } - Controller:
UserController或新的UserStatsController - Service: 计算用户本月已完成/已支付的充电次数和总消费。
- 响应示例:
- 前端修改 (
dashboard/page.tsx):- 移除相关模拟数据加载逻辑。
- 定义与后端响应匹配的数据接口。
- 使用
axios(api 实例) 并行调用上述两个接口。 - 根据真实数据更新UI组件的显示。
- 处理API错误和加载状态。
- 状态: 等待后端接口实现。
-
"我的充电记录" (
/my-sessions) 页面数据对接 (已完成)- 前端已修改为调用
POST /api/session/my/list/page接口。 - 后端
ChargingSessionController和ChargingSessionVO已确认基本满足需求。 - 状态: 已完成,等待用户测试确认。
- 前端已修改为调用
-
管理员仪表盘 (
/admin/dashboard) 框架 (已完成)- 包含系统概览统计卡片(模拟数据)和管理模块导航卡片。
- 状态: 初步完成,后续需对接真实统计数据和启用所有导航链接。
-
机器人管理页面 (
/admin/robots) 框架 (已完成)- 包含列表展示、搜索、筛选、分页(模拟数据)。
- 模拟的添加、编辑、删除按钮。
- 状态: 初步完成,后续需对接真实数据和实现CRUD操作。
4. 数据库 Schema (初步 DDL)
-- 用户表
CREATE TABLE `user` (
`id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
`username` VARCHAR(255) NOT NULL UNIQUE COMMENT '用户名',
`password` VARCHAR(255) NOT NULL COMMENT '密码 (加密存储)',
`role` VARCHAR(50) NOT NULL DEFAULT 'user' COMMENT '角色 (user/admin)',
`balance` DECIMAL(10, 2) NOT NULL DEFAULT 0.00 COMMENT '账户余额',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`is_deleted` TINYINT(1) DEFAULT 0 COMMENT '逻辑删除标志 (0:未删, 1:已删)'
) COMMENT='用户表';
-- 充电机器人表
CREATE TABLE `charging_robot` (
`id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
`robot_id` VARCHAR(100) NOT NULL UNIQUE COMMENT '机器人唯一标识符 (对应MQTT clientId)',
`status` VARCHAR(50) DEFAULT 'idle' COMMENT '状态 (idle, moving, charging, error, offline)',
`location` VARCHAR(255) COMMENT '当前位置描述 (如 base, near_P001, P001)',
`battery_level` INT COMMENT '电量百分比',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`is_deleted` TINYINT(1) DEFAULT 0 COMMENT '逻辑删除标志'
) COMMENT='充电机器人表';
-- 车位表
CREATE TABLE `parking_spot` (
`id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
`spot_id` VARCHAR(100) NOT NULL UNIQUE COMMENT '车位唯一标识符',
`location_desc` VARCHAR(255) COMMENT '位置描述',
`status` VARCHAR(50) DEFAULT 'available' COMMENT '状态 (available, occupied, maintenance)',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`is_deleted` TINYINT(1) DEFAULT 0 COMMENT '逻辑删除标志'
) COMMENT='车位表';
-- 充电记录表
CREATE TABLE `charging_session` (
`id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
`user_id` BIGINT NOT NULL COMMENT '用户ID',
`robot_id` VARCHAR(100) NOT NULL COMMENT '机器人ID',
`spot_id` VARCHAR(100) NOT NULL COMMENT '车位ID',
`start_time` DATETIME COMMENT '充电开始时间',
`end_time` DATETIME COMMENT '充电结束时间',
`duration_seconds` INT COMMENT '总充电时长 (秒)',
`cost` DECIMAL(10, 2) COMMENT '本次消费金额',
`status` VARCHAR(50) COMMENT '会话状态 (pending, robot_moving, charging, completed, error, cancelled)',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间'
) COMMENT='充电记录表';
-- 激活码表
CREATE TABLE `activation_code` (
`id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
`code` VARCHAR(100) NOT NULL UNIQUE COMMENT '激活码字符串',
`value` DECIMAL(10, 2) NOT NULL COMMENT '充值金额',
`is_used` TINYINT(1) DEFAULT 0 COMMENT '是否已使用 (0:未使用, 1:已使用)',
`user_id` BIGINT COMMENT '使用者ID (使用后)',
`use_time` DATETIME COMMENT '使用时间',
`expire_time` DATETIME COMMENT '过期时间 (可选)',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) COMMENT='激活码表';
注:以上 DDL 仅为初步设计,字段类型、长度、索引等可能需要根据实际情况调整。
5. API 设计 (高层规划)
- 认证:
POST /api/user/loginPOST /api/user/logout(可选, 使 Session 失效)
- 用户:
GET /api/user/current(获取当前用户信息,包括余额)GET /api/users(管理员 - 获取用户列表)
- 充电:
POST /api/charging/request(用户 - 发起充电请求,参数:spotId)POST /api/charging/stop(用户 - 请求停止当前充电)GET /api/charging/history(用户 - 查看自己的充电记录)GET /api/charging/sessions(管理员 - 查看所有充电记录)
- 激活码:
POST /api/codes/redeem(用户 - 使用激活码充值,参数:code)POST /api/codes(管理员 - 生成激活码,可选)
- 管理 (可选):
GET /api/robotsPOST /api/robotsGET /api/spotsPOST /api/spots
6. MQTT 消息契约
再次强调,requirements.md 中定义的 MQTT Topic 和 Payload 结构为 关键接口。必须与硬件开发团队 最终确认并严格遵守 此约定。任何变动都需要双方同步更新。
7. 后续步骤
- 等待用户提供:
- MQTT Broker 的详细连接信息 (URL, Port)。
- 用于指令 (
robot/command/{clientId}) 和状态 (robot/status/{clientId}) Topic 的用户名和密码。 - 与硬件团队确认最终的 MQTT Topic 和 Payload 结构。
- 开始开发: 在获取 MQTT 信息后,可以并行开始:
- 阶段一: 数据库初始化、用户模块开发、
robot_task相关基础 Service 开发。 - 阶段二: MQTT 配置、基础连接、订阅实现、集成
robot_task检查与更新逻辑、任务超时处理实现。
- 阶段一: 数据库初始化、用户模块开发、
- 按照开发阶段逐步推进。