用户
This commit is contained in:
233
springboot-init-main/doc/development_plan.md
Normal file
233
springboot-init-main/doc/development_plan.md
Normal file
@@ -0,0 +1,233 @@
|
||||
# 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. 开发阶段与模块划分
|
||||
|
||||
我们将开发过程划分为以下几个主要阶段/模块:
|
||||
|
||||
**阶段一:基础架构与用户管理**
|
||||
|
||||
1. **环境配置**: 确认 JDK, Maven, MySQL, Redis 环境。
|
||||
2. **数据库初始化**:
|
||||
* 根据 `requirements.md` 中的设计,创建数据库 `mqtt_charging_system` (或使用现有 `my_db` 并调整)。
|
||||
* 编写并执行 SQL DDL 脚本,创建 `user`, `charging_robot`, `parking_spot`, `charging_session`, `activation_code` 表(详细见第 4 节)。
|
||||
3. **用户模块实现**:
|
||||
* 创建 `User` 实体类 (`model/entity/User.java`),包含余额、角色等字段。
|
||||
* 创建 `UserMapper` 接口 (`mapper/UserMapper.java`)。
|
||||
* 创建 `UserService` 接口及实现 (`service/UserService.java`, `service/impl/UserServiceImpl.java`),包含用户注册、查询、余额更新等方法。
|
||||
* 实现密码存储:使用 Spring Security 的 `BCryptPasswordEncoder` 对密码进行加密。在 `config` 包下配置 `PasswordEncoder` Bean。
|
||||
* 创建 `UserController` (`controller/UserController.java`),提供登录接口。
|
||||
4. **认证与授权**:
|
||||
* 实现登录接口 `/api/user/login`。
|
||||
* 登录成功后,使用 Spring Session (已配置为 Redis 存储) 存储用户会话信息。
|
||||
* 后续请求通过 Session 进行用户身份验证。
|
||||
* 实现简单的基于角色的访问控制:在需要权限的 Controller 方法或 Service 方法上,通过检查当前登录用户的 `role` 字段进行判断。可以创建一个简单的 AOP 切面或直接在方法内判断。
|
||||
|
||||
**阶段二:MQTT 集成**
|
||||
|
||||
1. **配置 MQTT 连接**:
|
||||
* 在 `application.yml` 中添加 MQTT Broker 的 `url`, `username`, `password`, 以及用于命令和状态的 `commandTopic`, `statusTopic` 基础路径。**这些值需要用户提供。**
|
||||
* 创建 `MqttProperties.java` (`config/properties/MqttProperties.java`) 类,使用 `@ConfigurationProperties` 读取配置。
|
||||
2. **实现 MQTT 客户端**:
|
||||
* 创建 `MqttConfig.java` (`config/MqttConfig.java`)。
|
||||
* 配置 `MqttConnectOptions` Bean,设置用户名、密码、自动重连、清理会话等。
|
||||
* 配置 `MqttClient` Bean,并在应用启动后连接到 Broker。
|
||||
* **重点**: 实现 `MqttCallbackExtended` 接口,处理连接成功 (`connectComplete`)、连接丢失 (`connectionLost`)、消息到达 (`messageArrived`) 事件。
|
||||
* 在 `connectComplete` 中,订阅状态 Topic (`robot/status/+`)。`+` 是通配符,用于接收所有机器人的状态。
|
||||
* 在 `connectionLost` 中,记录日志并依赖 Paho 客户端的自动重连机制。
|
||||
* 在 `messageArrived` 中,将接收到的消息(JSON 字符串)传递给专门的消息处理服务。
|
||||
3. **实现消息发布与处理**:
|
||||
* 创建 `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` 更新机器人状态、处理充电完成事件等)。
|
||||
|
||||
**阶段三:核心充电业务逻辑**
|
||||
|
||||
1. **机器人与车位管理**:
|
||||
* 创建 `ChargingRobot` 和 `ParkingSpot` 实体、Mapper、Service、Controller。
|
||||
* 提供基础的 CRUD 接口供管理员使用(可选,根据 `requirements.md`)。
|
||||
* `ChargingRobotService` 需要包含更新机器人状态(如 `idle`, `moving`, `charging`, `error`)和位置的方法。
|
||||
2. **充电会话管理**:
|
||||
* 创建 `ChargingSession` 实体、Mapper、Service。
|
||||
3. **充电流程实现**:
|
||||
* 创建 `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` 消息的处理。
|
||||
4. **计费逻辑**:
|
||||
* 在 `UserService` 或单独的 `BillingService` 中实现计费方法。
|
||||
* 根据 `ChargingSession` 的总时长和预设的单价计算费用。
|
||||
* 调用 `UserService` 更新用户余额 (注意并发安全,可使用数据库行锁或乐观锁)。
|
||||
|
||||
**阶段四:激活码与完善**
|
||||
|
||||
1. **激活码模块**:
|
||||
* 创建 `ActivationCode` 实体、Mapper、Service。
|
||||
* 实现激活码生成逻辑(管理员功能,可在 Service 中提供)。
|
||||
* 创建 `ActivationCodeController.java` (`controller/ActivationCodeController.java`)。
|
||||
* 实现 `/api/codes/redeem` 接口:接收激活码,验证有效性,调用 `UserService` 更新用户余额,将激活码标记为已使用。
|
||||
2. **API 文档**:
|
||||
* 在所有 Controller 和 DTO 上添加 Knife4j (Swagger) 注解 (`@Api`, `@ApiOperation`, `@ApiModelProperty` 等)。
|
||||
3. **测试**:
|
||||
* 编写单元测试 (JUnit) 覆盖 Service 层核心逻辑。
|
||||
* 进行接口测试(使用 Postman 或类似工具)。
|
||||
* **难点**: MQTT 交互的测试,可能需要 Mock `MqttClient` 或搭建本地 MQTT Broker 进行集成测试。
|
||||
4. **错误处理与日志**:
|
||||
* 完善全局异常处理 (`GlobalExceptionHandler.java`)。
|
||||
* 在关键业务点添加详细日志(使用 SLF4j)。
|
||||
* **新增:实现任务超时处理逻辑。**
|
||||
* 创建 `TaskTimeoutHandler.java` (`service/TaskTimeoutHandler.java` 或类似名称)。
|
||||
* 使用 `@Scheduled` 注解创建一个定时任务,定期执行。
|
||||
* 在定时任务中,调用 `RobotTaskService` 查找状态为 `SENT` 且超时的任务。
|
||||
* 对于超时的任务,更新其状态为 `TIMED_OUT`,记录错误信息。
|
||||
* 根据业务需要,可能需要同步更新关联的 `charging_robot` 和 `charging_session` 的状态。
|
||||
|
||||
## 4. 数据库 Schema (初步 DDL)
|
||||
|
||||
```sql
|
||||
-- 用户表
|
||||
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/login`
|
||||
* `POST /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/robots`
|
||||
* `POST /api/robots`
|
||||
* `GET /api/spots`
|
||||
* `POST /api/spots`
|
||||
|
||||
## 6. MQTT 消息契约
|
||||
|
||||
再次强调,`requirements.md` 中定义的 MQTT Topic 和 Payload 结构为 **关键接口**。必须与硬件开发团队 **最终确认并严格遵守** 此约定。任何变动都需要双方同步更新。
|
||||
|
||||
## 7. 后续步骤
|
||||
|
||||
1. **等待用户提供**:
|
||||
* MQTT Broker 的详细连接信息 (URL, Port)。
|
||||
* 用于指令 (`robot/command/{clientId}`) 和状态 (`robot/status/{clientId}`) Topic 的用户名和密码。
|
||||
* 与硬件团队确认最终的 MQTT Topic 和 Payload 结构。
|
||||
2. **开始开发**: 在获取 MQTT 信息后,可以并行开始:
|
||||
* **阶段一**: 数据库初始化、用户模块开发、**`robot_task` 相关基础 Service 开发**。
|
||||
* **阶段二**: MQTT 配置、基础连接、订阅实现、**集成 `robot_task` 检查与更新逻辑**、**任务超时处理实现**。
|
||||
3. 按照开发阶段逐步推进。
|
||||
@@ -0,0 +1,152 @@
|
||||
# 开发阶段一:基础架构与用户管理
|
||||
|
||||
## 1. 目标
|
||||
|
||||
* 搭建项目基础运行环境。
|
||||
* 初始化数据库及核心用户表。
|
||||
* 实现用户实体、数据访问、业务逻辑和基础 API。
|
||||
* 实现安全的密码存储和用户登录认证机制 (基于 Session)。
|
||||
* 构建移动端 App 的基础框架和登录/注册页面。
|
||||
* 实现基于角色的基础页面导航。
|
||||
|
||||
## 2. 后端开发详解
|
||||
|
||||
1. **环境配置确认**:
|
||||
* 确保本地开发环境已安装并配置好 JDK 1.8, Maven, MySQL (5.7+), Redis。
|
||||
* 确认 Maven `settings.xml` 配置正确(如果使用私服)。
|
||||
* 在 IDE (如 IntelliJ IDEA) 中正确导入项目,确保依赖下载无误。
|
||||
2. **数据库初始化**:
|
||||
* 连接 MySQL 数据库。
|
||||
* 创建数据库,例如 `mqtt_charging_system` (字符集推荐 `utf8mb4`)。
|
||||
* **创建 `user` 表**: 执行 `development_plan.md` 中定义的 `user` 表 DDL 语句。
|
||||
```sql
|
||||
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='用户表';
|
||||
```
|
||||
* **配置数据源**: 检查并确认 `application.yml` 中的 `spring.datasource` 配置正确连接到创建的数据库。
|
||||
3. **用户模块实现**:
|
||||
* **Entity**: 创建 `src/main/java/com/yupi/project/model/entity/User.java`。使用 Lombok (`@Data`) 简化代码,使用 MyBatis-Plus 注解 (`@TableName`, `@TableId`, `@TableField`) 映射数据库字段,特别是驼峰与下划线。
|
||||
* **Mapper**: 创建 `src/main/java/com/yupi/project/mapper/UserMapper.java` 接口,继承 `BaseMapper<User>`。确保 `MyApplication.java` 中的 `@MapperScan` 包含此路径。
|
||||
* **Service**:
|
||||
* 创建 `src/main/java/com/yupi/project/service/UserService.java` 接口,继承 `IService<User>`,并定义业务方法,如 `userLogin(String username, String password)`, `userRegister(String username, String password, String checkPassword)`, `getCurrentUser(HttpServletRequest request)` 等。
|
||||
* 创建 `src/main/java/com/yupi/project/service/impl/UserServiceImpl.java` 实现类,注入 `UserMapper`。
|
||||
* 在注册逻辑中,添加参数校验(用户名、密码长度,两次密码是否一致),检查用户名是否已存在。
|
||||
* 在登录逻辑中,查询用户,校验密码。
|
||||
* **密码加密**:
|
||||
* 创建 `src/main/java/com/yupi/project/config/SecurityConfig.java` (或类似名称)。
|
||||
* 在该配置类中声明一个 `PasswordEncoder` Bean:
|
||||
```java
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
```
|
||||
* 在 `UserServiceImpl` 中注入 `PasswordEncoder`,在注册时对密码进行加密 (`passwordEncoder.encode(rawPassword)`),在登录时进行比对 (`passwordEncoder.matches(rawPassword, encodedPassword)`)。
|
||||
* **Controller**:
|
||||
* 创建 `src/main/java/com/yupi/project/controller/UserController.java`。
|
||||
* 注入 `UserService`。
|
||||
* 实现 `POST /api/user/login` 接口,调用 `UserService.userLogin`,登录成功后将用户信息存入 Session (Spring Session 会自动处理)。定义清晰的请求体 DTO (`UserLoginRequest`) 和响应体 (`BaseResponse<User>`)。
|
||||
* 实现 `POST /api/user/register` 接口 (可选,或仅限管理员创建),调用 `UserService.userRegister`。定义请求体 DTO (`UserRegisterRequest`)。
|
||||
* 实现 `GET /api/user/current` 接口,用于前端获取当前登录用户的信息(从 Session 中获取)。
|
||||
* 实现 `POST /api/user/logout` 接口,用于用户登出(使 Session 失效)。
|
||||
* **常量与异常**: 在 `constant` 包定义用户会话 Key。在 `exception` 包定义业务异常(如 `BusinessException`),并在 Service 层适当抛出(如用户名已存在、密码错误),由 `GlobalExceptionHandler` 统一处理。
|
||||
4. **认证与授权 (Session)**:
|
||||
* 确认 `pom.xml` 中包含 `spring-session-data-redis` 和 `spring-boot-starter-data-redis` 依赖。
|
||||
* 确认 `application.yml` 中 Redis 配置正确。
|
||||
* Spring Boot 会自动配置 Spring Session 使用 Redis。登录成功后,将用户信息(脱敏后,如移除密码)存入 `HttpSession`。
|
||||
* `session.setAttribute(UserConstant.USER_LOGIN_STATE, safeUser);`
|
||||
* 在需要登录才能访问的接口方法中,从 `HttpServletRequest` 获取 `HttpSession`,再获取用户信息。可以封装一个获取当前用户的方法在 `UserService` 中。
|
||||
* **基础角色检查**: 在需要区分管理员和普通用户的接口方法内部(或 Service 方法内部),获取当前登录用户的 `role` 字段进行判断。
|
||||
```java
|
||||
User currentUser = userService.getCurrentUser(request);
|
||||
if (!"admin".equals(currentUser.getRole())) {
|
||||
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
|
||||
}
|
||||
// ... admin logic ...
|
||||
```
|
||||
(更高级的权限控制可以使用 Spring Security 或 AOP,本阶段可先用简单方式)。
|
||||
|
||||
## 3. 前端 (手机版网页) 开发详解
|
||||
|
||||
* **技术选型**: **Next.js (基于 React)**。使用 Tailwind CSS 进行样式设计,确保响应式布局,优先适配移动端浏览器。
|
||||
* **目标**: 实现 Web App 基础框架、登录页、注册页(如果需要)、用户主页(区分角色)。
|
||||
* **UI 风格**: (与之前一致,但实现方式变为HTML/CSS)
|
||||
* **主色调**: 科技蓝 (#0A7AFF 或类似) 与白色 (#FFFFFF)。
|
||||
* **辅助色**: 浅灰 (#F0F0F0)、深灰 (#808080)。
|
||||
* **字体**: 无衬线字体,如系统默认字体,或引入网络字体如 Inter, Noto Sans SC。
|
||||
* **图标**: 简洁、线条化的科技风格图标 (可使用 SVG图标库如 Heroicons, Feather Icons, 或 Iconfont CSS)。
|
||||
* **布局**: 简洁、留白充足,控件圆角化。使用Flexbox和Grid进行响应式布局。
|
||||
* **动效**: 轻微、平滑的CSS过渡和动画,增强科技感。
|
||||
* **整体**: 保持所有页面风格统一。
|
||||
|
||||
* **项目结构 (Next.js App Router)**:
|
||||
* `charging_web_app/src/app/`:页面路由和布局。
|
||||
* `layout.tsx`: 全局根布局 (包含 `AuthProvider`)。
|
||||
* `login/page.tsx`: 登录页面。
|
||||
* `register/page.tsx`: 注册页面。
|
||||
* `(authenticated)/dashboard/page.tsx`: 普通用户主页 (示例路径,使用路由组进行权限控制)。
|
||||
* `(authenticated)/admin/dashboard/page.tsx`: 管理员主页 (示例路径)。
|
||||
* `charging_web_app/src/components/`: 可复用UI组件。
|
||||
* `charging_web_app/src/contexts/`: React Context (如 `AuthContext.tsx`)。
|
||||
* `charging_web_app/src/services/`: API服务 (如 `api.ts`)。
|
||||
* `charging_web_app/src/hooks/`: 自定义React Hooks。
|
||||
* `charging_web_app/src/lib/` (或 `utils`): 工具函数。
|
||||
* `charging_web_app/public/`: 静态资源。
|
||||
* `tailwind.config.ts`, `postcss.config.js`: Tailwind CSS 配置。
|
||||
|
||||
* **页面设计与流程 (Web)**:
|
||||
1. **登录页 (`src/app/login/page.tsx`)**:
|
||||
* **UI**: 蓝色顶部Banner或背景元素,白色内容区域。App Logo (可选)、用户名输入框 (`<input type="text">`)、密码输入框 (`<input type="password">`)、"登录"按钮 (`<button>`)(蓝色填充)、"注册"链接 (`<Link href="/register">`)。输入框和按钮使用Tailwind CSS进行样式化,具有圆角和科技感边框。
|
||||
* **交互**: 输入校验(非空)。点击登录按钮,显示加载指示器(如旋转SVG图标或文字提示)。
|
||||
* **API 调用**: 调用后端 `POST /api/user/login` (通过 `AuthContext` 中的 `login` 方法)。
|
||||
* **响应处理**:
|
||||
* 成功:`AuthContext` 更新用户状态。页面导航由 `AuthContext` 或页面逻辑处理 (例如,使用 `useRouter().push('/dashboard')`)。会话主要由后端的HttpOnly Cookie管理。
|
||||
* 失败:隐藏加载指示器,显示错误提示(如页面内红色文本)。
|
||||
2. **注册页 (`src/app/register/page.tsx`)**: (如果开放注册)
|
||||
* **UI**: 与登录页风格一致。包含用户名、密码、确认密码输入框,"注册"按钮。
|
||||
* **交互**: 输入校验(非空、密码一致性、格式要求等)。点击注册按钮。
|
||||
* **API 调用**: 调用后端 `POST /api/user/register` (通过 `AuthContext` 中的 `register` 方法)。
|
||||
* **响应处理**: 成功后可提示注册成功并跳转到登录页;失败则提示错误。
|
||||
3. **用户主页 (根据角色不同,示例路径 `src/app/(authenticated)/dashboard/page.tsx` 或 `src/app/(authenticated)/admin/dashboard/page.tsx`)**:
|
||||
* **UI**:
|
||||
* **普通用户**: 响应式导航栏(移动端可以是汉堡菜单或底部Tab模拟)。页面内容可包含欢迎信息、账户余额等。整体蓝白风格,卡片式布局。
|
||||
* **管理员**: 响应式导航栏(侧边栏或顶部菜单)。列表和数据展示为主,使用表格或卡片。
|
||||
* **功能**: 根据角色展示不同功能入口。
|
||||
* **API 调用**: 进入时 `AuthContext` 会尝试通过 `checkAuth` 获取最新用户信息。
|
||||
4. **导航与权限控制**:
|
||||
* 使用Next.js App Router进行页面路由。
|
||||
* 在根布局 `src/app/layout.tsx` 中包裹 `AuthProvider`。
|
||||
* 可以使用路由组 `(authenticated)` 结合中间件 ( `middleware.ts` ) 或在布局/页面组件中通过 `useAuth()` 检查认证状态和用户角色,实现页面级权限控制和未登录重定向。
|
||||
|
||||
* **状态管理**: 主要使用 `AuthContext` 管理用户登录状态和用户信息。对于其他全局状态,可按需引入 Zustand 或 Valtio 等轻量级状态管理库。
|
||||
* **网络请求**: 已封装 `src/services/api.ts` (Axios实例),处理 API Base URL、请求头(如 `withCredentials` 以支持Session Cookie)、加载状态、错误处理。
|
||||
* **Next.js代理**: 为了避免CORS问题和隐藏后端API实际地址,可以在 `next.config.mjs` 中配置rewrites代理,将前端的 `/api/*` 请求转发到后端Spring Boot服务的地址 (例如 `http://localhost:7529/api/*`)。
|
||||
|
||||
## 4. 本阶段交付物
|
||||
|
||||
* 可运行的后端 Spring Boot 项目。
|
||||
* 包含 `user` 表的数据库。
|
||||
* 实现用户注册(可选)、登录、获取当前用户信息、登出功能的后端 API。
|
||||
* 密码加密存储。
|
||||
* 基于 Redis 的 Session 认证机制。
|
||||
* **基础的Next.js手机版网页前端框架 (`charging_web_app`)。**
|
||||
* **实现登录、注册(可选)页面及功能的手机版网页。**
|
||||
* **根据用户角色导航到不同主页框架的手机版网页 (基础实现)。**
|
||||
* 简要的接口测试报告(如 Postman 截图,或浏览器开发者工具网络请求截图)。
|
||||
|
||||
## 5. 注意事项
|
||||
|
||||
* 密码绝不能明文存储或传输。
|
||||
* API 响应中避免返回用户密码等敏感信息。
|
||||
* 做好基础的参数校验。
|
||||
* 前后端接口地址、请求/响应格式需约定一致。
|
||||
* **确保Next.js项目正确配置了对后端API的代理,或在生产环境中通过Nginx等进行反向代理。**
|
||||
* **Tailwind CSS的正确配置和使用,以实现响应式和移动优先的UI。**
|
||||
@@ -0,0 +1,156 @@
|
||||
# 开发阶段二:MQTT 集成
|
||||
|
||||
## 1. 目标
|
||||
|
||||
* 在后端配置并建立与 MQTT Broker 的稳定连接。
|
||||
* 实现 MQTT 消息的订阅 (接收机器人状态) 和发布 (发送控制指令)。
|
||||
* 集成 `robot_task` 表逻辑,确保命令按顺序发送并跟踪状态。
|
||||
* 实现任务超时处理机制。
|
||||
* (前端暂无重大变更,主要为后端集成工作)。
|
||||
|
||||
## 2. 先决条件
|
||||
|
||||
* **MQTT Broker 信息**:
|
||||
* **地址**: 本项目将使用公共 MQTT Broker: `broker.emqx.io`
|
||||
* **TCP 端口**: `1883`
|
||||
* **连接认证**: 此公共 Broker 通常允许匿名连接,即连接时不需要提供用户名和密码。
|
||||
* **Topic 与 Payload 约定**: 必须与硬件团队最终确认 MQTT Topic 结构和 JSON Payload 格式。
|
||||
* **Topic 唯一性**: 由于使用公共 Broker,Topic 必须包含项目唯一标识前缀 (例如: `yupi_mqtt_power_project/`),以避免与其他用户冲突。最终结构如: `[项目唯一前缀]/robot/command/{clientId}` 和 `[项目唯一前缀]/robot/status/{clientId}`。
|
||||
* **应用层鉴权**: 鉴于公共 Broker 的特性,强烈建议在消息 Payload层面实现应用层鉴权机制,以确保消息的合法性和安全性。例如,机器人上报状态时携带特定令牌,后端发送指令时也包含可供机器人验证的令牌或签名。
|
||||
|
||||
## 3. 后端开发详解
|
||||
|
||||
1. **数据库初始化**:
|
||||
* **创建 `robot_task` 表**: 执行 `development_plan.md` 中定义的 `robot_task` 表 DDL 语句。
|
||||
```sql
|
||||
CREATE TABLE `robot_task` (
|
||||
`id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
|
||||
`robot_id` VARCHAR(100) NOT NULL COMMENT '机器人ID',
|
||||
`command_type` VARCHAR(50) NOT NULL COMMENT '命令类型 (MOVE_TO_SPOT, START_CHARGE, STOP_CHARGE, QUERY_STATUS)',
|
||||
`command_payload` TEXT COMMENT '命令参数 (JSON格式)',
|
||||
`status` VARCHAR(50) NOT NULL DEFAULT 'PENDING' COMMENT '任务状态 (PENDING, SENT, ACKNOWLEDGED_SUCCESS, ACKNOWLEDGED_FAILURE, TIMED_OUT)',
|
||||
`sent_time` DATETIME COMMENT '命令发送时间',
|
||||
`ack_time` DATETIME COMMENT '命令确认时间',
|
||||
`related_session_id` BIGINT COMMENT '关联的充电会话ID (可选)',
|
||||
`error_message` TEXT COMMENT '失败或超时的错误信息',
|
||||
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
INDEX `idx_robot_status` (`robot_id`, `status`) COMMENT '机器人和状态索引,便于查询'
|
||||
) COMMENT='机器人指令任务表';
|
||||
```
|
||||
2. **配置 MQTT 连接 (`application.yml`)**:
|
||||
* 添加 MQTT 相关配置项 (根据用户提供的信息填写):
|
||||
```yaml
|
||||
mqtt:
|
||||
broker-url: tcp://broker.emqx.io:1883 # 公共 Broker 地址
|
||||
username: # 通常为空,此公共 Broker 无需连接认证
|
||||
password: # 通常为空
|
||||
client-id-prefix: backend-yupi-mqtt-power- # 项目唯一客户端ID前缀
|
||||
default-qos: 1
|
||||
connection-timeout: 30
|
||||
keep-alive-interval: 60
|
||||
command-topic-base: yupi_mqtt_power_project/robot/command # 包含项目唯一前缀的指令Topic基础路径
|
||||
status-topic-base: yupi_mqtt_power_project/robot/status # 包含项目唯一前缀的状态Topic基础路径
|
||||
```
|
||||
* 创建 `src/main/java/com/yupi/project/config/properties/MqttProperties.java` 类,使用 `@ConfigurationProperties(prefix = "mqtt")` 和 Lombok (`@Data`) 来映射这些配置。
|
||||
3. **实现 MQTT 客户端 (`MqttConfig.java`)**:
|
||||
* 创建 `src/main/java/com/yupi/project/config/MqttConfig.java`。
|
||||
* **注入 `MqttProperties`**。
|
||||
* **创建 `MqttConnectOptions` Bean**:
|
||||
* 设置 `userName`, `password` (如果配置了)。
|
||||
* 设置 `isAutomaticReconnect(true)`。
|
||||
* 设置 `isCleanSession(true)` (或根据需要设置为 `false` 以接收离线消息)。
|
||||
* 设置 `connectionTimeout`, `keepAliveInterval`。
|
||||
* **创建 `MqttClient` Bean**:
|
||||
* 生成唯一的 `clientId` (例如: `properties.getClientIdPrefix() + UUID.randomUUID().toString()`)。
|
||||
* 创建 `MqttClient` 实例 (`new MqttClient(properties.getBrokerUrl(), clientId, new MemoryPersistence())`)。
|
||||
* **创建 `MqttCallbackExtended` Bean (核心)**:
|
||||
* 创建一个类实现 `MqttCallbackExtended` (例如, `MqttCallbackHandler`)。
|
||||
* **注入 `MqttMessageHandler` 服务** (将在下一步创建)。
|
||||
* 实现 `connectComplete(boolean reconnect, String serverURI)` 方法:
|
||||
* 在此方法中订阅状态主题: `client.subscribe(properties.getStatusTopicBase() + "/+", properties.getDefaultQos());` (`+` 是单层通配符)。记录订阅成功日志。
|
||||
* 如果是重连 (`reconnect == true`),可能需要执行一些状态同步逻辑(例如查询所有机器人的状态)。
|
||||
* 实现 `connectionLost(Throwable cause)` 方法:
|
||||
* 记录连接丢失的错误日志。
|
||||
* Paho 客户端的 `automaticReconnect` 会尝试重连。
|
||||
* 实现 `messageArrived(String topic, MqttMessage message)` 方法:
|
||||
* 获取 `payload` (`new String(message.getPayload())`)。
|
||||
* **应用层鉴权**: 在此步骤或 `mqttMessageHandler.handleStatusUpdate` 内部,应校验 `payload` 中携带的机器人身份标识或令牌,确保消息来源可靠。
|
||||
* 调用 `mqttMessageHandler.handleStatusUpdate(topic, payload)` 处理消息。
|
||||
* **注意**: 此方法应快速完成,避免阻塞回调线程。复杂的处理应异步进行或在 `handleStatusUpdate` 内部处理。
|
||||
* 实现 `deliveryComplete(IMqttDeliveryToken token)` 方法 (可选): 消息发布完成后的回调,通常用于 QoS 1 或 2 的确认。
|
||||
* **连接 MQTT Broker**:
|
||||
* 为 `MqttClient` Bean 设置初始化方法 (`initMethod="connectBroker"`) 或使用 `@PostConstruct`。
|
||||
* 在连接方法中:
|
||||
* 设置 `MqttCallback` (`client.setCallback(mqttCallbackHandlerBean)`)。
|
||||
* 调用 `client.connect(mqttConnectOptionsBean)`。
|
||||
* 添加适当的异常处理 (如 `MqttException`)。
|
||||
4. **实现 `RobotTask` 管理 (`RobotTaskService`)**:
|
||||
* 创建 `src/main/java/com/yupi/project/model/entity/RobotTask.java`。
|
||||
* 创建 `src/main/java/com/yupi/project/mapper/RobotTaskMapper.java`。
|
||||
* 创建 `src/main/java/com/yupi/project/service/RobotTaskService.java` 接口,定义方法如:
|
||||
* `hasPendingSentTask(String robotId)`: 检查是否有 `SENT` 状态的任务。
|
||||
* `createTask(String robotId, String commandType, String payloadJson, Long sessionId)`: 创建 `PENDING` 任务。
|
||||
* `markTaskAsSent(Long taskId)`: 更新任务状态为 `SENT`。
|
||||
* `findSentTask(String robotId, String expectedResponseType)`: 根据机器人响应查找对应的 `SENT` 任务 (需要逻辑判断响应类型对应哪个命令)。
|
||||
* `markTaskAsAcknowledged(Long taskId, boolean success, String errorMessage)`: 更新任务状态为 `ACKNOWLEDGED_SUCCESS` 或 `ACKNOWLEDGED_FAILURE`。
|
||||
* `findAndMarkTimedOutTasks(int timeoutSeconds)`: 查找并标记超时任务。
|
||||
* 创建 `src/main/java/com/yupi/project/service/impl/RobotTaskServiceImpl.java` 实现类。
|
||||
5. **实现消息发布 (`MqttService`)**:
|
||||
* 创建 `src/main/java/com/yupi/project/service/MqttService.java` 接口,定义 `sendCommand(...)` 方法。
|
||||
* 创建 `src/main/java/com/yupi/project/service/impl/MqttServiceImpl.java` 实现类。
|
||||
* 注入 `MqttClient`, `MqttProperties`, `RobotTaskService`。
|
||||
* 实现 `sendCommand(...)` 方法:
|
||||
* 调用 `robotTaskService.hasPendingSentTask(robotId)` 检查。
|
||||
* 调用 `robotTaskService.createTask(...)` 创建任务。
|
||||
* 构造 Topic (`properties.getCommandTopicBase() + "/" + robotId`)。
|
||||
* **应用层鉴权**: 构造 `payloadJson` 时,应包含可供机器人验证的令牌或签名,确保指令的合法性。
|
||||
* 调用 `mqttClient.publish(topic, payloadJson.getBytes(), properties.getDefaultQos(), false)` 发送。
|
||||
* 发送成功后,调用 `robotTaskService.markTaskAsSent(taskId)`。
|
||||
* 添加 `MqttException` 处理。
|
||||
6. **实现消息处理 (`MqttMessageHandler`)**:
|
||||
* 创建 `src/main/java/com/yupi/project/service/MqttMessageHandler.java` (接口和实现)。
|
||||
* 注入 `RobotTaskService`, `Gson` (或其他 JSON 库), 以及后续阶段需要的 Service (如 `ChargingService`, `ChargingRobotService`)。
|
||||
* 实现 `handleStatusUpdate(String topic, String payload)` 方法:
|
||||
* 解析 `topic` 提取 `robotId`。
|
||||
* 解析 `payload` (JSON) 为 `RobotStatusDTO` (需要创建此 DTO)。
|
||||
* **核心**: 根据 `RobotStatusDTO` 的内容判断这是对哪个命令的响应 (或者是一个主动状态上报)。调用 `robotTaskService.findSentTask(...)` 查找对应的 `SENT` 任务。
|
||||
* 如果找到任务,调用 `robotTaskService.markTaskAsAcknowledged(...)` 更新任务状态。
|
||||
* **只有在任务确认后**,才根据 `RobotStatusDTO.status` 调用后续业务逻辑 Service 的方法 (例如: `chargingService.handleRobotArrival(robotId, spotId)`, `chargingService.handleChargeCompletion(robotId, duration)`)。
|
||||
* 处理 JSON 解析异常、任务未找到等情况。
|
||||
7. **实现任务超时处理 (`TaskTimeoutHandler`)**:
|
||||
* 创建 `src/main/java/com/yupi/project/schedule/TaskTimeoutHandler.java` (或放在 service 包)。
|
||||
* 注入 `RobotTaskService`, 以及可能需要更新状态的 `ChargingRobotService`, `ChargingSessionService`。
|
||||
* 添加 `@Component` 和 `@EnableScheduling` (在主启动类或配置类上)。
|
||||
* 创建一个方法,使用 `@Scheduled(fixedRate = 60000)` (例如每分钟执行一次)。
|
||||
* 在该方法中:
|
||||
* 调用 `robotTaskService.findAndMarkTimedOutTasks(timeoutSeconds)` (例如超时设为 120 秒)。
|
||||
* 获取超时的任务列表。
|
||||
* 对每个超时任务,根据业务逻辑更新关联的 `charging_robot` (如设为 `offline` 或 `error`) 和 `charging_session` (如设为 `error`) 的状态。
|
||||
* 添加日志记录。
|
||||
|
||||
## 4. 前端 (移动端 App) 开发详解
|
||||
|
||||
* **本阶段前端无核心功能变更。**
|
||||
* 可以考虑在界面上预留位置,用于未来展示机器人的连接状态或基本信息,但这依赖于后端是否通过 API 提供这些聚合信息,或者前端是否也直接订阅 MQTT (通常不推荐前端直接连 Broker)。
|
||||
* 主要工作是等待后端完成 MQTT 集成,为下一阶段的核心业务功能提供基础。
|
||||
|
||||
## 5. 本阶段交付物
|
||||
|
||||
* 包含 `robot_task` 表的数据库。
|
||||
* 配置完成并能在启动时自动连接 MQTT Broker 的后端服务。
|
||||
* 实现 MQTT 状态消息订阅和命令消息发布的功能。
|
||||
* 集成 `robot_task` 表的命令发送检查和状态跟踪逻辑。
|
||||
* 实现任务超时检查和处理的定时任务。
|
||||
* 必要的单元测试 (例如 `RobotTaskService` 的逻辑)。
|
||||
* 描述 MQTT 配置和运行状态的简要文档或日志。
|
||||
|
||||
## 6. 注意事项
|
||||
|
||||
* MQTT 连接信息 (特别是密码,如果未来使用私有 Broker) 应妥善保管,避免硬编码在代码中,使用配置文件管理。
|
||||
* **Topic 设计与唯一性**: 在公共 Broker 环境下,Topic 设计必须包含项目唯一标识前缀,以防止命名冲突。所有参与方(后端、机器人)都必须使用此约定。
|
||||
* **Payload 格式**: 必须严格遵守与硬件端的约定。
|
||||
* **应用层鉴权**: 由于使用公共 Broker,强烈建议在消息 Payload 中实现双向的应用层鉴权机制。后端需验证机器人状态的来源,机器人需验证指令的来源。
|
||||
* `MqttCallback` 中的 `messageArrived` 必须快速返回,避免阻塞。
|
||||
* 超时时间的设置需要根据实际网络情况和机器人响应时间进行调整。
|
||||
* 健壮的错误处理对于 MQTT 集成至关重要。
|
||||
@@ -0,0 +1,142 @@
|
||||
# 开发阶段三:核心充电业务逻辑
|
||||
|
||||
## 1. 目标
|
||||
|
||||
* 实现充电机器人和车位的基础管理功能 (如果需要管理员界面)。
|
||||
* 实现完整的充电请求、机器人调度、充电开始/结束、状态跟踪和计费流程。
|
||||
* 将业务逻辑与 MQTT 消息处理紧密结合。
|
||||
* 在移动端实现发起充电、查看充电状态和历史记录的核心用户功能。
|
||||
* 在移动端为管理员提供基础的监控界面(可选)。
|
||||
|
||||
## 2. 后端开发详解
|
||||
|
||||
1. **数据库初始化**:
|
||||
* **创建 `charging_robot`, `parking_spot`, `charging_session` 表**: 执行 `development_plan.md` 中定义的相应 DDL 语句。
|
||||
```sql
|
||||
-- (DDL for charging_robot)
|
||||
CREATE TABLE `charging_robot` (...) COMMENT='充电机器人表';
|
||||
-- (DDL for parking_spot)
|
||||
CREATE TABLE `parking_spot` (...) COMMENT='车位表';
|
||||
-- (DDL for charging_session)
|
||||
CREATE TABLE `charging_session` (...) COMMENT='充电记录表';
|
||||
```
|
||||
2. **机器人与车位管理 (可选,主要面向管理员)**:
|
||||
* **Entity**: 创建 `ChargingRobot.java`, `ParkingSpot.java`。
|
||||
* **Mapper**: 创建 `ChargingRobotMapper.java`, `ParkingSpotMapper.java`。
|
||||
* **Service**: 创建 `ChargingRobotService.java`, `ParkingSpotService.java` 接口和实现类。
|
||||
* `ChargingRobotService` 需包含方法:`findAvailableRobot()`, `updateRobotStatus(String robotId, String status, String location, Integer batteryLevel)`。
|
||||
* `ParkingSpotService` 需包含方法:`updateSpotStatus(String spotId, String status)`。
|
||||
* **Controller (可选)**: 创建 `ChargingRobotController.java`, `ParkingSpotController.java`,提供 CRUD 接口 (`/api/robots`, `/api/spots`),并进行管理员角色检查。
|
||||
3. **充电会话管理**:
|
||||
* **Entity**: 创建 `ChargingSession.java`。
|
||||
* **Mapper**: 创建 `ChargingSessionMapper.java`。
|
||||
* **Service**: 创建 `ChargingSessionService.java` 接口和实现类。包含创建、更新状态、记录时间/费用、查询用户历史记录等方法。
|
||||
4. **充电流程实现**:
|
||||
* **创建 `ChargingService` (核心业务编排)**: 创建接口 `ChargingService.java` 和实现 `ChargingServiceImpl.java`。
|
||||
* 注入 `UserService`, `ChargingRobotService`, `ParkingSpotService`, `ChargingSessionService`, `MqttService`, `RobotTaskService`。
|
||||
* 定义核心方法,如:
|
||||
* `requestCharging(String userId, String spotId)`: 处理用户充电请求。
|
||||
* `handleRobotArrival(String robotId, String spotId)`: 处理机器人到达事件。
|
||||
* `handleChargingStart(String robotId, String spotId)`: 处理充电开始事件。
|
||||
* `handleChargeUpdate(String robotId, int durationSeconds)`: 处理充电中状态更新 (可选)。
|
||||
* `handleChargeCompletion(String robotId, String spotId, int totalDurationSeconds)`: 处理充电完成事件。
|
||||
* `handleRobotError(String robotId, String errorCode, String message)`: 处理机器人错误。
|
||||
* `stopChargingByUser(String userId)`: 处理用户停止请求。
|
||||
* **修改 `MqttMessageHandler`**:
|
||||
* 在其 `handleStatusUpdate` 方法中,当 `robot_task` 确认成功后,根据收到的机器人状态 (`RobotStatusDTO.status`),调用 `ChargingService` 中对应的 `handleXxx` 方法。
|
||||
* 例如:收到 `arrived_at_spot` 状态 -> 调用 `chargingService.handleRobotArrival(...)`。
|
||||
* 例如:收到 `charge_complete` 状态 -> 调用 `chargingService.handleChargeCompletion(...)`。
|
||||
* **实现 `ChargingServiceImpl` 核心逻辑**:
|
||||
* `requestCharging`:
|
||||
1. 检查用户余额。
|
||||
2. 查找可用机器人 (`chargingRobotService.findAvailableRobot()`)。
|
||||
3. 查找车位信息。
|
||||
4. 创建 `ChargingSession` 记录 (状态 `PENDING`)。
|
||||
5. 调用 `mqttService.sendCommand(robotId, "move_to_spot", payload, sessionId)` 发送移动指令。
|
||||
6. 如果发送成功,更新 `ChargingSession` 状态为 `ROBOT_MOVING`,更新机器人/车位DB状态。
|
||||
* `handleRobotArrival`:
|
||||
1. 更新机器人DB状态。
|
||||
2. 调用 `mqttService.sendCommand(robotId, "start_charge", null, sessionId)` 发送开始充电指令。
|
||||
* `handleChargingStart`:
|
||||
1. 更新机器人DB状态。
|
||||
2. 更新 `ChargingSession` 状态为 `CHARGING`,记录 `start_time`。
|
||||
* `handleChargeCompletion`:
|
||||
1. 更新机器人DB状态为 `IDLE`。
|
||||
2. 更新车位DB状态为 `AVAILABLE`。
|
||||
3. 记录 `end_time`, `totalDurationSeconds` 到 `ChargingSession`。
|
||||
4. **调用计费逻辑** (见下一点)。
|
||||
5. 更新 `ChargingSession` 状态为 `COMPLETED`。
|
||||
* `handleRobotError`: 更新相关实体的状态为 `ERROR`,记录错误信息。
|
||||
* `stopChargingByUser`: 查找用户当前的 `CHARGING` 会话,获取 `robotId`,调用 `mqttService.sendCommand(robotId, "stop_charge", null, sessionId)`。后续计费在收到 `charge_complete` 时处理。
|
||||
* **创建 `ChargingController`**:
|
||||
* 注入 `ChargingService`。
|
||||
* 实现 `POST /api/charging/request` 接口: 调用 `chargingService.requestCharging(...)`。
|
||||
* 实现 `POST /api/charging/stop` 接口: 调用 `chargingService.stopChargingByUser(...)`。
|
||||
* 实现 `GET /api/charging/history` 接口: 调用 `chargingSessionService.getUserHistory(...)`。
|
||||
* 实现 `GET /api/charging/sessions` 接口 (管理员): 调用 `chargingSessionService.getAllSessions(...)`。
|
||||
5. **计费逻辑**:
|
||||
* 在 `ChargingServiceImpl.handleChargeCompletion` 中实现。
|
||||
* 定义计费单价 (元/秒 或 元/小时,需转换)。可以配置在 `application.yml` 中或作为常量。
|
||||
* 计算费用: `cost = totalDurationSeconds * pricePerSecond`。
|
||||
* 调用 `userService.deductBalance(userId, cost)`。 **此方法需要处理并发**,例如使用 SQL `UPDATE user SET balance = balance - ? WHERE id = ? AND balance >= ?` 来保证原子性扣款。
|
||||
* 将计算出的 `cost` 记录到 `ChargingSession`。
|
||||
|
||||
## 3. 前端 (移动端 App) 开发详解
|
||||
|
||||
* **目标**: 实现用户发起充电、查看状态、停止充电、查看历史的核心交互。
|
||||
|
||||
* **页面设计与流程**:
|
||||
1. **首页/充电页 (`ChargeScreen.js`)**: (普通用户 Tab 之一)
|
||||
* **UI**:
|
||||
* 顶部显示用户当前余额 (`GET /api/user/current`)。
|
||||
* 地图或列表展示附近可用的充电车位 (`GET /api/spots?status=available`,需要后端支持此查询)。车位用蓝色空闲图标表示。
|
||||
* (可选)显示可用机器人的数量或状态 (`GET /api/robots?status=idle`)。
|
||||
* 选择车位后,弹窗或按钮显示 "发起充电"。
|
||||
* **交互**: 用户点击选择一个可用车位,然后点击 "发起充电" 按钮。
|
||||
* **API 调用**: 点击按钮后,调用 `POST /api/charging/request`,参数为 `{ spotId: selectedSpotId }`。
|
||||
* **响应处理**: 成功则提示 "机器人正在前往...",并导航到充电状态页;失败(如机器人忙、余额不足)则提示用户。
|
||||
2. **充电状态页 (`ChargingStatusScreen.js`)**:
|
||||
* **UI**:
|
||||
* 醒目位置显示当前充电状态(如 "机器人移动中", "正在充电", "充电完成", "错误"),可以使用不同颜色和图标(蓝色移动、绿色充电、灰色完成、红色错误)。
|
||||
* 显示目标车位 ID。
|
||||
* 显示使用的机器人 ID。
|
||||
* 如果是 "正在充电" 状态,显示已充电时长(需要后端通过 WebSocket 或轮询 API 提供实时数据,本阶段可先只显示开始时间或总状态)。
|
||||
* 显示 "停止充电" 按钮。
|
||||
* 科技感的进度条或动画效果展示充电过程。
|
||||
* **交互**: 用户可以点击 "停止充电" 按钮。
|
||||
* **API 调用**:
|
||||
* 进入页面时,需要获取当前正在进行的充电会话信息(后端需要提供接口,如 `GET /api/charging/current`)。
|
||||
* (难点)实时状态更新:
|
||||
* **方案一 (轮询)**: 前端定时 (如 5-10 秒) 调用 `GET /api/charging/current` 刷新状态和时长。
|
||||
* **方案二 (WebSocket)**: 后端在状态变更时通过 WebSocket 推送消息给前端。这需要额外集成 WebSocket。**本方案优先考虑轮询**。
|
||||
* 点击 "停止充电" 时,调用 `POST /api/charging/stop`。
|
||||
* **响应处理**: "停止充电" 请求成功后,等待状态更新为 "充电完成" 或 "错误"。
|
||||
3. **充电历史页 (`HistoryScreen.js`)**: (普通用户 Tab 之一, "我的" 页面内)
|
||||
* **UI**: 列表展示用户的充电记录。每条记录显示车位 ID、机器人 ID、开始/结束时间、时长、费用。按时间倒序排列。蓝白卡片风格。
|
||||
* **交互**: 下拉刷新,上拉加载更多。
|
||||
* **API 调用**: 进入页面时调用 `GET /api/charging/history` (支持分页参数)。
|
||||
4. **管理员监控页 (`AdminMonitorScreen.js`)**: (管理员 Tab 之一)
|
||||
* **UI**:
|
||||
* 列表展示所有机器人状态 (`GET /api/robots`)。
|
||||
* 列表展示所有车位状态 (`GET /api/spots`)。
|
||||
* 列表展示所有进行中/最近的充电会话 (`GET /api/charging/sessions`)。
|
||||
* 数据使用不同颜色区分状态 (如 idle/available-蓝色, moving/charging/occupied-橙色/绿色, error/maintenance-红色)。
|
||||
* **交互**: 实时刷新数据(同样可通过轮询或 WebSocket)。
|
||||
|
||||
## 4. 本阶段交付物
|
||||
|
||||
* 包含 `charging_robot`, `parking_spot`, `charging_session` 表的数据库。
|
||||
* 实现充电请求处理、机器人状态更新、充电会话管理、计费逻辑的后端服务。
|
||||
* 充电相关核心业务 API (`/api/charging/*`)。
|
||||
* (可选) 机器人和车位管理的 CRUD API。
|
||||
* 移动端 App 实现选择车位、发起充电、查看充电状态(轮询)、停止充电、查看历史记录的功能。
|
||||
* (可选) 移动端 App 实现管理员监控界面。
|
||||
* 覆盖核心业务逻辑的单元测试。
|
||||
* 接口测试报告。
|
||||
|
||||
## 5. 注意事项
|
||||
|
||||
* 计费逻辑和余额扣减必须保证数据一致性和准确性,考虑并发问题。
|
||||
* 状态转换逻辑要清晰、完备,覆盖所有正常和异常情况。
|
||||
* 前后端对于充电状态的定义和展示需要统一。
|
||||
* 实时状态更新的实现方式(轮询/WebSocket)需要权衡复杂度和实时性要求。
|
||||
@@ -0,0 +1,102 @@
|
||||
# 开发阶段四:激活码与完善
|
||||
|
||||
## 1. 目标
|
||||
|
||||
* 实现激活码生成、管理和兑换充值功能。
|
||||
* 完善 API 文档。
|
||||
* 增强系统测试覆盖率。
|
||||
* 优化错误处理和日志记录。
|
||||
* 对移动端 App 进行 UI 细节打磨和体验优化。
|
||||
|
||||
## 2. 后端开发详解
|
||||
|
||||
1. **数据库初始化**:
|
||||
* **创建 `activation_code` 表**: 执行 `development_plan.md` 中定义的 `activation_code` 表 DDL 语句。
|
||||
```sql
|
||||
CREATE TABLE `activation_code` (...) COMMENT='激活码表';
|
||||
```
|
||||
2. **激活码模块实现**:
|
||||
* **Entity**: 创建 `ActivationCode.java`。
|
||||
* **Mapper**: 创建 `ActivationCodeMapper.java`。
|
||||
* **Service**: 创建 `ActivationCodeService.java` 接口和实现类。
|
||||
* `generateCodes(int count, BigDecimal value, Date expireTime)`: (管理员) 批量生成激活码。生成逻辑可以使用 UUID 或自定义规则,确保唯一性。将生成的 Code 和 Value 存入数据库。
|
||||
* `redeemCode(String userId, String code)`: (用户) 兑换激活码。
|
||||
1. 查询 `code` 是否存在、是否未使用 (`is_used = 0`)、是否过期 (如果 `expire_time` 不为空)。
|
||||
2. 如果有效,获取其 `value`。
|
||||
3. 调用 `userService.addBalance(userId, value)` 增加用户余额 (注意并发)。
|
||||
4. 将激活码标记为已使用 (`is_used = 1`), 记录 `user_id`, `use_time`。
|
||||
5. 返回成功或失败信息。
|
||||
* (可选) `listCodes(Page page, ActivationCodeQuery query)`: (管理员) 查询激活码列表。
|
||||
* **Controller**: 创建 `ActivationCodeController.java`。
|
||||
* 实现 `POST /api/codes/redeem` 接口: 调用 `activationCodeService.redeemCode(...)`。
|
||||
* (可选) 实现 `POST /api/codes` 接口 (管理员): 调用 `activationCodeService.generateCodes(...)`。
|
||||
* (可选) 实现 `GET /api/codes` 接口 (管理员): 调用 `activationCodeService.listCodes(...)`。
|
||||
3. **API 文档完善 (Knife4j/Swagger)**:
|
||||
* 检查所有 Controller 类和方法,确保添加了 `@Api`, `@ApiOperation` 注解,描述清晰。
|
||||
* 检查所有请求 DTO 和响应 VO (视图对象),确保添加了 `@ApiModel`, `@ApiModelProperty` 注解,描述字段含义和是否必需。
|
||||
* 启动项目,访问 Knife4j 文档地址 (通常是 `/doc.html`),检查文档是否完整、准确。
|
||||
4. **测试增强**:
|
||||
* **单元测试**: 使用 JUnit 和 Mockito (或 PowerMock) 对 Service 层的核心业务逻辑进行测试,特别是计费、余额变更、激活码兑换、任务状态处理等。
|
||||
* **集成测试 (可选但推荐)**:
|
||||
* 使用 Spring Boot Test (`@SpringBootTest`) 测试 Service 层与数据库的交互。
|
||||
* **MQTT 集成测试**: 搭建本地 MQTT Broker (如 Docker 版 EMQX 或 Mosquitto),编写测试用例模拟机器人发送状态消息,验证 `MqttMessageHandler` 和后续业务逻辑是否正确处理。
|
||||
* **接口测试**: 使用 Postman 或 Apifox 等工具,对所有 API 进行测试,覆盖正常和异常场景。
|
||||
5. **错误处理与日志优化**:
|
||||
* **全局异常处理**: 检查 `GlobalExceptionHandler.java`,确保捕获了常见的业务异常 (`BusinessException`) 和系统异常,并返回统一格式的错误响应给前端。
|
||||
* **日志**: 使用 SLF4j + Logback (Spring Boot 默认)。
|
||||
* 在关键路径 (如用户登录、发起充电、收到 MQTT 消息、计费、余额变更、任务超时) 添加 `info` 级别的日志。
|
||||
* 在异常捕获处添加 `error` 级别的日志,包含堆栈信息。
|
||||
* 考虑异步记录日志 (如果性能要求高)。
|
||||
* 配置 `logback-spring.xml`,区分不同环境的日志级别和输出目的地(控制台、文件)。
|
||||
|
||||
## 3. 前端 (移动端 App) 开发详解
|
||||
|
||||
* **目标**: 实现激活码兑换功能,优化用户体验和界面细节。
|
||||
|
||||
* **页面设计与流程**:
|
||||
1. **激活码兑换页 (`RedeemCodeScreen.js`)**: (普通用户,通常在 "我的" 页面内入口)
|
||||
* **UI**: 简洁风格。一个输入框用于输入激活码,一个 "兑换" 按钮 (蓝色)。可以加一些提示文字说明。
|
||||
* **交互**: 输入激活码,点击兑换按钮,显示加载状态。
|
||||
* **API 调用**: 调用 `POST /api/codes/redeem`,参数 `{ code: enteredCode }`。
|
||||
* **响应处理**: 成功则提示 "兑换成功,增加余额 X 元",并更新本地用户余额显示;失败则提示错误原因(如无效码、已使用、已过期)。
|
||||
2. **用户中心/我的页面 (`ProfileScreen.js`)**:
|
||||
* **UI**: 优化布局,清晰展示用户名、角色、**账户余额**。
|
||||
* 提供 "充电历史"、"激活码兑换" 等功能的入口。
|
||||
* 提供 "退出登录" 按钮。
|
||||
* 整体风格保持蓝白科技感。
|
||||
3. **UI 细节打磨**:
|
||||
* 检查所有页面的 UI 元素对齐、间距、颜色、字体是否符合设计规范。
|
||||
* 优化加载状态的显示(如骨架屏、更平滑的加载动画)。
|
||||
* 优化错误提示方式,使其更友好、清晰。
|
||||
* 确保在不同尺寸和分辨率的手机屏幕上显示良好(响应式布局)。
|
||||
* 增加必要的过渡动画,提升操作流畅感和科技感。
|
||||
4. **管理员激活码管理页 (`AdminCodeManagementScreen.js`)**: (管理员)
|
||||
* **UI**:
|
||||
* 提供生成激活码的功能入口(输入数量、金额、过期时间)。
|
||||
* 列表展示已生成的激活码,包含码字、金额、状态(未使用/已使用/已过期)、使用者、使用时间等信息。
|
||||
* 支持搜索、筛选、分页。
|
||||
* **API 调用**: 调用 `/api/codes` (POST 生成, GET 查询)。
|
||||
|
||||
* **代码优化**:
|
||||
* 检查代码结构,封装可复用组件。
|
||||
* 处理潜在的内存泄漏。
|
||||
* 优化性能,特别是列表渲染。
|
||||
|
||||
## 4. 本阶段交付物
|
||||
|
||||
* 包含 `activation_code` 表的数据库。
|
||||
* 实现激活码生成、查询、兑换功能的后端服务及 API。
|
||||
* 完善的 Knife4j API 文档。
|
||||
* 更高覆盖率的单元测试和集成测试报告。
|
||||
* 优化后的错误处理和日志配置。
|
||||
* 移动端 App 实现激活码兑换功能。
|
||||
* 移动端 App 整体 UI/UX 优化版本。
|
||||
* (可选) 移动端 App 实现管理员激活码管理界面。
|
||||
* 最终的接口测试报告。
|
||||
|
||||
## 5. 注意事项
|
||||
|
||||
* 激活码生成要保证唯一性。
|
||||
* 激活码兑换和余额增加操作要保证原子性。
|
||||
* API 文档应与实际代码保持同步。
|
||||
* 测试应尽可能覆盖边界条件和异常情况。
|
||||
37
springboot-init-main/doc/kaiti.md
Normal file
37
springboot-init-main/doc/kaiti.md
Normal file
@@ -0,0 +1,37 @@
|
||||
## 项目目标与核心技术
|
||||
|
||||
本项目的主要设计目标是打造一款能够 **自主导航、避障、充电和远程控制** 的智能机器人。
|
||||
|
||||
* **核心控制器**: ESP32-CAM,提供计算与无线通信能力。
|
||||
* **通信方式**: 通过 Wi-Fi 连接 MQTT 服务器(提及使用 **EMQX**),实现与云端(即我们的后端平台)的实时数据交互。
|
||||
* **导航与避障**: 配备红外循迹模块和超声波避障模块,实现自主导航和避障。
|
||||
* **运动控制**: 舵机控制系统用于调整运动方向与停车位置。
|
||||
* **充电控制**: 继电器控制充电桩(或充电接口)的开关。
|
||||
* **报警系统**: 蜂鸣器用于状态提示或报警。
|
||||
|
||||
## 远程控制与监控
|
||||
|
||||
* 机器人通过 MQTT 协议将实时状态和数据上传到云端。
|
||||
* 用户可以通过 **手机APP或网页**(由我们的后端平台支持)随时查看并远程控制机器人的运行。
|
||||
|
||||
## 技术挑战 (硬件/嵌入式侧重点)
|
||||
|
||||
* 智能导航与避障算法实现 (利用红外与超声波)。
|
||||
* 充电与停车的精准对接。
|
||||
* MQTT 通信的稳定性和安全性 (硬件端)。
|
||||
* 声光报警的准确性。
|
||||
|
||||
## 技术路线总结
|
||||
|
||||
* **系统定位**: 基于 MQTT 协议的停车场自助充电机器人。
|
||||
* **核心硬件**: ESP32-CAM。
|
||||
* **关键技术**: 红外循迹、超声波避障、舵机控制、MQTT 通信。
|
||||
* **核心功能**: 自主导航、定点停车、自助充电、远程监控。
|
||||
* **价值**: 提升停车场管理效率和用户体验,实现智能化与自动化。
|
||||
|
||||
## 设计关键内容
|
||||
|
||||
* **硬件集成**: ESP32-CAM 与各传感器、执行器的集成。
|
||||
* **控制算法**: 导航、避障、停靠、充电逻辑 (嵌入式实现)。
|
||||
* **数据传输与管理**: 通过 Wi-Fi 和 MQTT 与远程控制端(后端平台)进行数据交互。
|
||||
* **安全与提醒**: 通过 MQTT 上报状态,蜂鸣器进行本地报警。
|
||||
124
springboot-init-main/doc/requirements.md
Normal file
124
springboot-init-main/doc/requirements.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# MQTT 智能充电桩系统 - 需求文档
|
||||
|
||||
## 1. 项目概述
|
||||
|
||||
本项目旨在开发一个基于 Spring Boot 的 **移动式** 智能充电管理系统。系统作为后端平台,通过 MQTT 协议与 **基于 ESP32-CAM 的充电机器人** 硬件设备进行通信,实现远程控制、状态监控以及 **调度机器人到指定车位进行充电**。同时,系统提供用户管理、充值计费等功能。**平台软件不负责机器人底层的导航、避障、循迹等具体算法实现**,仅发送高级指令并接收处理后的状态。
|
||||
|
||||
## 2. 功能需求
|
||||
|
||||
### 2.1 用户管理与认证
|
||||
|
||||
* **用户角色**: 系统需支持两种用户角色:
|
||||
* 管理员:拥有系统所有管理权限。
|
||||
* 普通用户:可进行充电、充值等操作。
|
||||
* **用户登录**: 提供安全的用户名密码登录机制。
|
||||
* 区分管理员和普通用户登录接口或逻辑。
|
||||
* **用户信息管理** (管理员权限):
|
||||
* 查看用户列表。
|
||||
* (可选)添加、编辑、删除用户信息。
|
||||
|
||||
### 2.2 MQTT 集成与设备控制
|
||||
|
||||
* **MQTT 连接**:
|
||||
* 连接至 **指定的公共 MQTT 服务器 (如 EMQX,需提供具体地址和端口)**。
|
||||
* 支持为特定的 MQTT 主题(Topic)设置用户名和密码进行认证 **(需提供)**。
|
||||
* 配置用于指令下发和状态上报的 Topic。
|
||||
* **消息格式**:
|
||||
* 与充电机器人通过 JSON 字符串格式进行消息交互。
|
||||
* **设备控制** (平台侧重点):
|
||||
* 向充电机器人发送 **高级控制指令**(见 MQTT Topic 规划)。
|
||||
* 平台 **不处理** 原始传感器数据(如红外、超声波读数)。
|
||||
* **状态接收** (平台侧重点):
|
||||
* 接收充电机器人上报的 **综合状态信息**(见 MQTT Topic 规划)。
|
||||
|
||||
### 2.3 智能充电核心业务
|
||||
|
||||
* **充电机器人管理** (管理员权限):
|
||||
* (可选)添加、查看、管理充电机器人设备信息(如:设备ID、当前状态、位置)。
|
||||
* **车位管理** (管理员权限):
|
||||
* (可选)管理可用的充电车位信息(如:车位ID、位置描述)。
|
||||
* **激活码充值**:
|
||||
* 普通用户可以使用激活码为自己的账户充值。
|
||||
* 系统需验证激活码的有效性,并将对应金额或时长添加到用户账户。
|
||||
* 需要设计激活码生成和管理机制(管理员)。
|
||||
* **充电计费**:
|
||||
* 根据实际充电时长进行计费。
|
||||
* 需要明确计费单价(例如:元/小时)。
|
||||
* 充电结束后,从用户账户扣除相应费用。
|
||||
* 用户账户余额不足时,应有相应处理(如:无法启动充电、自动停止充电)。
|
||||
* **充电流程** (平台侧重点):
|
||||
1. 用户登录系统。
|
||||
2. 用户 **选择目标充电车位** `{parkingSpotId}`。
|
||||
3. 用户发起充电请求。
|
||||
4. 系统检查用户余额。
|
||||
5. 系统选择一个可用的充电机器人 `{robotId}` (如果系统管理多个机器人)。
|
||||
6. 系统通过 MQTT 发送 **移动指令 (`move_to_spot`)** 给机器人 `{robotId}`,目标为车位 `{parkingSpotId}`。
|
||||
7. 机器人移动到位后,通过 MQTT 上报 `arrived_at_spot` 状态。
|
||||
8. 系统通过 MQTT 发送 **启动充电指令 (`start_charge`)** 给机器人。
|
||||
9. 机器人开始充电并通过 MQTT 上报 `charging` 状态(及充电时长)。
|
||||
10. 用户发起停止充电请求 或 机器人上报 `charge_complete` / `error` 状态。
|
||||
11. 系统通过 MQTT 发送 **停止充电指令 (`stop_charge`)** 给机器人。
|
||||
12. 系统根据 MQTT 上报的 **总充电时长** 计算费用并扣费。
|
||||
13. 记录充电日志。
|
||||
|
||||
### 2.4 充电记录
|
||||
|
||||
* 记录用户的充电历史,包括:用户、**使用的机器人**、**充电车位**、开始时间、结束时间、充电时长、消费金额。
|
||||
* 普通用户可查看自己的充电记录。
|
||||
* 管理员可查看所有充电记录。
|
||||
|
||||
## 3. 非功能需求
|
||||
|
||||
* **可用性**: 系统应稳定可靠,提供持续服务。
|
||||
* **安全性**: 用户密码需加密存储;MQTT 通道需进行安全认证;防止未授权访问。
|
||||
* **可维护性**: 代码结构清晰,遵循高内聚低耦合原则,易于维护和扩展。
|
||||
* **性能**: 系统应能及时响应用户请求和处理 MQTT 消息。
|
||||
|
||||
## 4. 技术选型(初步)
|
||||
|
||||
* 后端框架: Spring Boot
|
||||
* 持久层: MyBatis-Plus
|
||||
* 数据库: MySQL
|
||||
* 缓存: Redis
|
||||
* **MQTT Broker**: 外部公共服务 (如 EMQX, **需用户提供实例信息**)
|
||||
* MQTT 客户端库: Eclipse Paho MQTT Client
|
||||
* API 文档: Knife4j
|
||||
* 数据交换格式: JSON
|
||||
* **机器人控制器 (参考)**: ESP32-CAM
|
||||
|
||||
## 5. MQTT 通道(Topic)规划 (需要用户确认并提供细节)
|
||||
|
||||
* **基础 Topic 结构**: (建议)
|
||||
* 指令下发: `robot/command/{clientId}`
|
||||
* 状态上报: `robot/status/{clientId}`
|
||||
* *注:`{clientId}` 通常是认证时使用的客户端ID,可以等同于机器人ID `{robotId}`,需要与硬件端约定。*
|
||||
|
||||
* **指令下发 Topic**: `robot/command/{clientId}` (平台 -> 机器人)
|
||||
* **认证**: **需要** (用户名/密码由用户提供)
|
||||
* **Payload (JSON 示例)**:
|
||||
* 移动到车位: `{"action": "move_to_spot", "spotId": "P001"}`
|
||||
* 开始充电: `{"action": "start_charge"}`
|
||||
* 停止充电: `{"action": "stop_charge"}`
|
||||
* 查询状态: `{"action": "query_status"}` (可选)
|
||||
|
||||
* **状态上报 Topic**: `robot/status/{clientId}` (机器人 -> 平台)
|
||||
* **认证**: **需要** (用户名/密码由用户提供)
|
||||
* **Payload (JSON 示例)**:
|
||||
* 空闲状态: `{"robotId": "R001", "status": "idle", "location": "base", "battery": 95}`
|
||||
* 移动中: `{"robotId": "R001", "status": "moving", "targetSpot": "P001", "battery": 90}`
|
||||
* 到达车位: `{"robotId": "R001", "status": "arrived_at_spot", "spotId": "P001", "battery": 88}`
|
||||
* 充电中: `{"robotId": "R001", "status": "charging", "spotId": "P001", "durationSeconds": 120, "battery": 92}`
|
||||
* 充电完成: `{"robotId": "R001", "status": "charge_complete", "spotId": "P001", "totalDurationSeconds": 3600, "battery": 100}`
|
||||
* 错误状态: `{"robotId": "R001", "status": "error", "errorCode": "NAV_BLOCKED", "message": "Navigation path blocked", "location": "near_P002"}`
|
||||
* 通用状态响应 (对 query_status): `{"robotId": "R001", "status": "idle", "location": "base", "battery": 95}`
|
||||
|
||||
* **重要提示**: 上述 Topic 结构和 Payload 内容为 **建议**,最终需要与硬件(ESP32)开发侧 **详细约定** 并 **统一**。平台将根据约定的格式进行开发。
|
||||
|
||||
## 6. 数据库设计 (已精简)
|
||||
|
||||
* **用户表 (`user`)**: 包含用户基本信息、**密码**、**角色 (如: admin, user)**、**账户余额**。
|
||||
* 充电机器人表 (`charging_robot`)
|
||||
* 车位表 (`parking_spot`)
|
||||
* 充电记录表 (`charging_session`)
|
||||
* 激活码表 (`activation_code`)
|
||||
* **机器人任务表 (`robot_task`)**: 用于跟踪发送给机器人的指令及其状态 (pending, sent, acknowledged_success, acknowledged_failure, timed_out),防止重复发送指令给未响应的机器人。
|
||||
Reference in New Issue
Block a user