# 开发阶段三：核心充电业务逻辑

## 1. 目标

*   实现充电机器人和车位的基础管理功能 (如果需要管理员界面)。
*   实现完整的充电请求、机器人调度、充电开始/结束、状态跟踪和计费流程。
*   将业务逻辑与 MQTT 消息处理紧密结合。
*   在移动端实现发起充电、查看充电状态和历史记录的核心用户功能。
*   在移动端为管理员提供基础的监控界面（可选）。

## 2. 后端开发详解

1.  **数据库初始化**:
    *   **创建 `charging_robot`, `parking_spot`, `charging_session` 表**: 执行以下 DDL 语句。
        ```sql
        -- DDL for charging_robot
        CREATE TABLE `charging_robot` (
          `id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
          `robot_uid` VARCHAR(100) NOT NULL UNIQUE COMMENT '机器人唯一标识符 (例如，硬件序列号)',
          `status` VARCHAR(50) NOT NULL DEFAULT 'IDLE' COMMENT '机器人状态 (IDLE, MOVING, CHARGING, ERROR, MAINTENANCE, OFFLINE)',
          `current_location` VARCHAR(255) COMMENT '当前位置描述 (例如，坐标或区域ID)',
          `battery_level` TINYINT UNSIGNED COMMENT '电池电量百分比 (0-100)',
          `current_task_id` BIGINT COMMENT '当前执行的任务ID (关联 robot_task.id)',
          `last_heartbeat_time` DATETIME 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 INDEX `idx_robot_uid` ON `charging_robot` (`robot_uid`);
        -- CREATE INDEX `idx_robot_status` ON `charging_robot` (`status`);

        -- DDL for parking_spot
        CREATE TABLE `parking_spot` (
          `id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
          `spot_uid` VARCHAR(100) NOT NULL UNIQUE COMMENT '车位唯一标识符 (例如，车位号)',
          `status` VARCHAR(50) NOT NULL DEFAULT 'AVAILABLE' COMMENT '车位状态 (AVAILABLE, OCCUPIED_BY_CAR, RESERVED, CHARGING, MAINTENANCE, UNAVAILABLE)',
          `location_description` VARCHAR(255) COMMENT '位置描述 (例如，楼层、区域)',
          `robot_assignable` BOOLEAN DEFAULT TRUE COMMENT '此车位是否可指派机器人前往',
          `current_session_id` BIGINT COMMENT '当前占用此车位的充电会话ID (关联 charging_session.id)',
          `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 INDEX `idx_spot_uid` ON `parking_spot` (`spot_uid`);
        -- CREATE INDEX `idx_spot_status` ON `parking_spot` (`status`);

        -- DDL for charging_session
        CREATE TABLE `charging_session` (
          `id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID，充电会话唯一标识',
          `user_id` BIGINT NOT NULL COMMENT '用户ID (关联 user.id)',
          `robot_id` BIGINT COMMENT '服务的机器人ID (关联 charging_robot.id)',
          `robot_uid_snapshot` VARCHAR(100) COMMENT '服务机器人的UID快照 (冗余字段，方便查询历史)',
          `spot_id` BIGINT NOT NULL COMMENT '车位ID (关联 parking_spot.id)',
          `spot_uid_snapshot` VARCHAR(100) NOT NULL COMMENT '车位UID快照 (冗余字段)',
          `status` VARCHAR(50) NOT NULL DEFAULT 'REQUESTED' COMMENT '充电会话状态 (REQUESTED, ROBOT_ASSIGNED, ROBOT_MOVING, CHARGING_STARTED, CHARGING_IN_PROGRESS, COMPLETED, CANCELLED_BY_USER, CANCELLED_BY_SYSTEM, ERROR, ROBOT_TASK_TIMEOUT)',
          `request_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '用户请求充电时间',
          `robot_assigned_time` DATETIME COMMENT '机器人分配时间',
          `robot_arrival_time` DATETIME COMMENT '机器人到达车位时间',
          `charge_start_time` DATETIME COMMENT '充电开始时间',
          `charge_end_time` DATETIME COMMENT '充电结束时间',
          `total_duration_seconds` INT COMMENT '总充电时长 (秒)',
          `energy_consumed_kwh` DECIMAL(10, 3) COMMENT '消耗电量 (kWh)',
          `cost` DECIMAL(10, 2) COMMENT '本次充电费用 (元)',
          `payment_status` VARCHAR(50) DEFAULT 'PENDING' COMMENT '支付状态 (PENDING, PAID, FAILED)',
          `error_code` VARCHAR(100) COMMENT '错误码 (如果会话出错)',
          `error_message` TEXT COMMENT '错误信息',
          `related_robot_task_id` BIGINT COMMENT '启动本次会话或关键步骤的 RobotTask ID',
          `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='充电记录表';

        -- (外键约束和索引建议)
        -- ALTER TABLE `charging_session` ADD CONSTRAINT `fk_session_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`);
        -- CREATE INDEX `idx_session_user` ON `charging_session` (`user_id`);
        -- ... (其他索引)
        ```
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` 接口和实现类。包含创建、更新状态、记录时间/费用、查询用户历史记录等方法。
        *   **注意**: 实现此 Service 后，需要回顾第二阶段的 `TaskTimeoutHandler.java`，补充当 `RobotTask` 超时后，调用此 Service 更新关联 `ChargingSession` 状态的逻辑。
    *   **创建 `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(...)`。
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）需要权衡复杂度和实时性要求。 