# cloud-platform-multiple-instructions-html **Repository Path**: chenqian1995/cloud-platform-multiple-instructions-html ## Basic Information - **Project Name**: cloud-platform-multiple-instructions-html - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-03-29 - **Last Updated**: 2026-03-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 多指令下发系统 > 车辆 CAN 文件串行下发管理系统 - 严谨专业版架构设计 基于 **Spring Boot 2.7 + MyBatis + MySQL + Redis** 实现的高可靠指令下发系统,支持批量指令串行执行、超时管理、设备在线检测等功能。 ## 📋 目录 - [功能特性](#-功能特性) - [技术栈](#-技术栈) - [系统架构](#-系统架构) - [指令状态机](#-指令状态机) - [超时调度机制](#-超时调度机制) - [数据库设计](#-数据库设计) - [Redis 数据结构](#-redis 数据结构) - [API 接口](#-api 接口) - [快速开始](#-快速开始) - [扩展点](#-扩展点) - [设计决策](#-设计决策) ## ✨ 功能特性 | 功能 | 说明 | |------|------| | **批量指令下发** | 支持单 VIN 批量提交多条指令 | | **串行执行** | 车辆一次只执行一条指令,保证顺序 | | **状态机管理** | 9 种指令状态,严格的状态流转控制 | | **超时调度** | Redis ZSet 时间轮实现 3 级超时管理 | | **分布式锁** | Redisson 分布式锁防止并发处理 | | **在线检测** | 设备在线/离线状态实时检测与处理 | | **定时轮询** | 每 3 秒检查超时任务 | ## 🛠️ 技术栈 | 组件 | 版本 | 说明 | |------|------|------| | Java | 8 | 基础语言 | | Spring Boot | 2.7.18 | 应用框架 | | MyBatis | 2.3.1 | ORM 框架 | | MySQL | 8.0+ | 数据持久化 | | Redis | 5.0+ | 缓存 + 超时调度 | | Redisson | 3.21.3 | 分布式锁 | | Lombok | 1.18.26 | 代码简化 | ## 🏗️ 系统架构 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 指令调度系统架构 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ 指令提交层 │ │ 状态管理层 │ │ 超时调度层 │ │ │ │ Controller │ │ Service │ │ Scheduler │ │ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ Redis 层 │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │ 分布式锁 │ │ ZSet超时队列 │ │ 在线状态缓存 │ │ │ │ │ │ lock:* │ │ zset:cmd:* │ │ online:* │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ MySQL 持久化 │ │ │ │ can_file_list_upload 表 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ 在线检测层 │ │ 指令执行层 │ │ │ │ Vehicle │ │ File │ │ │ │ Online │ │ Transfer │ │ │ └──────┬───────┘ └──────┬───────┘ │ │ │ │ │ │ ▼ ▼ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 外部接口:MQTT/HTTP 心跳检测 │ │ │ └─────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ ``` ### 核心组件 | 组件 | 职责 | |------|------| | `MultiCommandController` | API 接口层,处理 HTTP 请求 | | `MultiCommandService` | 业务逻辑层,指令调度核心 | | `CommandTimeoutManager` | 超时管理,基于 Redis ZSet | | `CommandScheduler` | 定时任务,每 3 秒轮询 | | `VehicleOnlineStatusService` | 车辆在线状态检测(需实现) | | `CanFileListUploadMapper` | 数据访问层 | ## 🔄 指令状态机 ### 状态定义 | 状态码 | 状态名 | 说明 | |-------|--------|------| | 0 | INIT | 初始化/等待传输 | | 1 | SUCCESS | 成功 | | 2 | FAIL | 失败 | | 3 | WAIT_TRANSFER | 等待下发 | | 4 | FILE_TRANSFERRING | 文件传输中 | | 5 | TRANSFER_PAUSED_OFFLINE | 传输暂停(设备离线) | | 6 | TRANSFER_INTERRUPTED_OFFLINE_2H | 传输中断(离线超过 2 小时) | | 7 | TRANSFER_INTERRUPTED_TIMEOUT | 传输中断(响应超时 3 分钟) | | 8 | ISSUE_TIMEOUT_24H | 下发超时(排队超过 24 小时) | ### 状态流转图 ``` ┌──────────┐ │ INIT │ 0: 初始化 │ (0) │ └────┬─────┘ │ 提交指令 ▼ ┌──────────┐ │WAIT_ │ 3: 等待下发 │TRANSFER │ │ (3) │ └────┬─────┘ │ ┌────┴────┬─────────────────┬──────────────┐ │ │ │ │ │ 24h 超时 │ 设备在线 │ 设备离线 │ │ │ │ │ ▼ ▼ ▼ ▼ ┌────────┐ ┌───────────┐ ┌────────────┐ ┌────────────┐ │ISSUE_ │ │FILE_ │ │TRANSFER_ │ │ 等待上线 │ │TIMEOUT │ │TRANSFERRING│ │PAUSED │ │ │ │ (8) │ │ (4) │ │OFFLINE (5) │ │ │ └────────┘ └─────┬─────┘ └─────┬──────┘ │ │ │ │ │ │ ┌────┴────┐ │ 2h 超时 │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ ┌────────────┐ │ │ │ │ │TRANSFER_ │ │ │ │ │ │INTERRUPTED │ │ │ │ │ │OFFLINE_2H │ │ │ │ │ │ (6) │ │ │ │ │ └────────────┘ │ │ │ │ │ │ 3min 超时 │ 上线后继续 │ │ │ │ │ ▼ │ │ │ ┌────────────┐ │ │ │ │ TRANSFER_ │ │ │ │ │ INTERRUPTED│ │ │ │ │ TIMEOUT │ │ │ │ │ (7) │ │ │ │ └────────────┘ │ │ │ │ │ │ ┌──────┴──────┐ │ │ │ │ │ │ ▼ ▼ │ │ ┌─────────┐ ┌──────────┐ │ │ │ SUCCESS │ │ FAIL │ │ │ │ (1) │ │ (2) │ │ │ └─────────┘ └──────────┘ └────────────┘ ``` ### 状态流转规则 ```java // 合法状态转换 INIT(0) → WAIT_TRANSFER(3) WAIT_TRANSFER(3) → FILE_TRANSFERRING(4) | ISSUE_TIMEOUT_24H(8) FILE_TRANSFERRING(4) → SUCCESS(1) | FAIL(2) | TRANSFER_PAUSED_OFFLINE(5) | TRANSFER_INTERRUPTED_TIMEOUT(7) TRANSFER_PAUSED_OFFLINE(5) → FILE_TRANSFERRING(4) | TRANSFER_INTERRUPTED_OFFLINE_2H(6) ``` ## ⏰ 超时调度机制 ### 三级超时管理 | 超时类型 | 时间 | 触发条件 | 状态变更 | |---------|------|---------|---------| | **3 分钟执行超时** | 180s | 指令开始执行后 3 分钟未完成 | 4→7 (在线) 或 4→5 (离线) | | **2 小时离线超时** | 7200s | 设备离线超过 2 小时 | 5→6 | | **24 小时排队超时** | 86400s | 指令创建后 24 小时未开始执行 | 0/3→8 | ### Redis ZSet 时间轮设计 ``` ZSet 结构: ┌─────────────────────────────────────────┐ │ zset:cmd:timeout:3min │ │ score: 超时时间戳 (createTime + 3min) │ │ value: commandId │ └─────────────────────────────────────────┘ ┌─────────────────────────────────────────┐ │ zset:cmd:timeout:2h │ │ score: 超时时间戳 (offlineTime + 2h) │ │ value: commandId │ └─────────────────────────────────────────┘ ┌─────────────────────────────────────────┐ │ zset:cmd:timeout:24h │ │ score: 超时时间戳 (createTime + 24h) │ │ value: commandId │ └─────────────────────────────────────────┘ ``` ### 定时任务调度 ```java @Scheduled(fixedRateString = "${multi-instruction.scheduler-interval-ms:3000}") public void checkTimeouts() { // 每 3 秒执行 // 1. 查询 3 分钟超时 ZSet // 2. 查询 2 小时离线 ZSet // 3. 查询 24 小时排队 ZSet // 4. 调用对应处理方法 } ``` ## 📊 数据库设计 ### 表结构 ```sql CREATE TABLE `can_file_list_upload` ( `id` int(11) NOT NULL AUTO_INCREMENT, `vin` varchar(32) NOT NULL COMMENT '车辆 VIN 码', `file_name` varchar(255) NOT NULL COMMENT '文件名称', `command_time` datetime NOT NULL COMMENT '指令时间', `file_type` tinyint(4) NOT NULL COMMENT '文件类型 1:file 2:dir', `file_size` int(11) DEFAULT '0' COMMENT '文件大小 (字节)', `file_url` varchar(512) NOT NULL COMMENT '文件地址', `progress` int(11) DEFAULT '0' COMMENT '传输进度 0-100', `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '状态', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `remark` varchar(512) DEFAULT NULL COMMENT '备注', `command_index` int(11) NOT NULL COMMENT '指令序号 (串行执行顺序)', `service_type` tinyint(4) NOT NULL DEFAULT '1' COMMENT '服务类型', `retry_count` int(11) DEFAULT '0' COMMENT '重试次数', `last_execute_time` datetime DEFAULT NULL COMMENT '最后执行时间', `offline_start_time` datetime DEFAULT NULL COMMENT '离线开始时间', PRIMARY KEY (`id`), KEY `idx_vin_status` (`vin`,`status`), KEY `idx_vin_index` (`vin`,`command_index`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='CAN 文件上传指令表'; ``` ### 索引设计 | 索引名 | 字段 | 用途 | |-------|------|------| | PRIMARY | id | 主键查询 | | idx_vin_status | vin, status | 按 VIN 和状态查询 | | idx_vin_index | vin, command_index | 按 VIN 和序号排序 | ## 🔑 Redis 数据结构 | Key 模式 | 类型 | 说明 | TTL | |---------|------|------|-----| | `lock:command:trigger:{vin}` | Lock | VIN 指令触发锁 | 10s | | `lock:command:timeout:3min:{id}` | Lock | 3 分钟超时处理锁 | 5s | | `lock:command:timeout:2h:{id}` | Lock | 2 小时超时处理锁 | 5s | | `lock:command:timeout:24h:{id}` | Lock | 24 小时超时处理锁 | 5s | | `zset:cmd:timeout:3min` | ZSet | 3 分钟超时队列 | - | | `zset:cmd:timeout:2h` | ZSet | 2 小时离线队列 | - | | `zset:cmd:timeout:24h` | ZSet | 24 小时排队队列 | - | | `online:vin:{vin}` | String | 车辆在线状态缓存 | 10s | ## 🌐 API 接口 ### 1. 批量下发指令 ```http POST /api/v1/multi-command/issue Content-Type: application/json { "vin": "32131231231231231", "paths": ["/data/file1.can", "/data/file2.can", "/data/file3.can"] } ``` **响应:** ```json { "success": true, "message": "指令下发成功", "commandCount": 3, "commands": [...] } ``` ### 2. 查询指令列表 ```http GET /api/v1/multi-command/list?vin=32131231231231231 ``` ### 3. 查询指令详情 ```http GET /api/v1/multi-command/detail/{id} ``` ### 4. 手动触发下一条指令 ```http POST /api/v1/multi-command/trigger-next?vin=32131231231231231 ``` ### 5. 更新车辆在线状态 ```http POST /api/v1/vehicle/online-status?vin=32131231231231231&isOnline=true ``` ## 🚀 快速开始 ### 1. 环境要求 - JDK 8+ - MySQL 8.0+ - Redis 5.0+ - Maven 3.6+ ### 2. 数据库初始化 ```bash mysql -u root -p < src/main/resources/schema.sql ``` ### 3. 配置修改 编辑 `src/main/resources/application.yml`: ```yaml spring: datasource: url: jdbc:mysql://localhost:3306/cloud_platform?useSSL=false&serverTimezone=Asia/Shanghai username: your_username password: your_password driver-class-name: com.mysql.cj.jdbc.Driver redis: host: localhost port: 6379 password: "" database: 0 multi-instruction: scheduler-interval-ms: 3000 # 定时任务间隔 command-exec-timeout-ms: 180000 # 3 分钟执行超时 offline-interrupt-threshold-ms: 7200000 # 2 小时离线中断 global-issue-timeout-ms: 86400000 # 24 小时全局超时 ``` ### 4. 启动应用 ```bash mvn clean install mvn spring-boot:run ``` ### 5. 接口测试 ```bash # 批量下发指令 curl -X POST http://localhost:8080/api/v1/multi-command/issue \ -H "Content-Type: application/json" \ -d '{ "vin": "32131231231231231", "paths": ["/data/file1.can", "/data/file2.can"] }' # 查询指令列表 curl http://localhost:8080/api/v1/multi-command/list?vin=32131231231231231 ``` ## 🔌 扩展点 ### 1. 文件传输实现 `MultiCommandServiceImpl.simulateFileTransfer()` 需要替换为实际传输逻辑: ```java // TODO: 替换为实际文件传输服务 private void simulateFileTransfer(CanFileListUploadDao command) { // 实际业务中调用文件传输服务 // fileTransferService.transfer(command.getFileUrl(), command.getVin()); } ``` ### 2. 在线状态检测 实现 `VehicleOnlineStatusService` 接口: ```java @Service public class VehicleOnlineStatusServiceImpl implements VehicleOnlineStatusService { @Override public boolean isOnline(String vin) { // 对接 MQTT 心跳或 HTTP 接口 // return mqttService.getLastHeartbeat(vin) > 30000; } @Override public void updateOnlineStatus(String vin, boolean isOnline) { // 更新缓存 } } ``` ### 3. 服务类型扩展 当前支持 4 种服务类型,可在 `ServiceTypeEnum` 中扩展: ```java public enum ServiceTypeEnum { TSP_NOPKI(1, "tspnopki"), TSP_PKI(2, "tsppki"), FIND_NOPKI(3, "findnopki"), FIND_PKI(4, "findpki"); } ``` ## 💡 设计决策 ### 为什么使用 Redis ZSet 实现超时调度? | 方案 | 优点 | 缺点 | |------|------|------| | **Redis ZSet** | 高性能、支持范围查询、天然有序 | 需要定期清理 | | 延迟队列 | 实现简单 | 不支持批量查询 | | 时间轮算法 | 内存占用低 | 实现复杂 | | 数据库轮询 | 实现简单 | 性能差、有延迟 | **选择 ZSet 的原因:** - `rangeByScore(minScore, maxScore)` 可一次性获取所有超时任务 - score 为时间戳,天然按超时时间排序 - 支持分布式环境,多实例共享超时队列 ### 为什么需要分布式锁? ``` 场景:多实例部署时,定时任务同时触发 实例 A: checkTimeouts() → 获取超时指令列表 → 处理 实例 B: checkTimeouts() → 获取超时指令列表 → 处理 (重复处理!) 解决:每个超时处理加分布式锁 lock:command:timeout:3min:{commandId} ``` ### 为什么状态机需要严格流转控制? - 防止状态跳跃(如 INIT 直接到 SUCCESS) - 保证业务逻辑正确性 - 便于问题排查和审计 ## 📝 测试 ```bash # 单元测试 mvn test -Dtest=MultiCommandServiceImplTest # 集成测试(需要 Redis) mvn test -Dtest=CommandTimeoutManagerIntegrationTest # ZSet 生命周期测试 mvn test -Dtest=ZSetLifecycleTest ``` ## 📁 项目结构 ``` cloud-platform-multiple-instruction/ ├── src/ │ ├── main/ │ │ ├── java/com/cloudplatform/multiinstruction/ │ │ │ ├── entity/ # 实体类 │ │ │ ├── enums/ # 枚举类 │ │ │ ├── mapper/ # MyBatis Mapper │ │ │ ├── service/ # 服务层 │ │ │ ├── controller/ # 控制器 │ │ │ ├── config/ # 配置类 │ │ │ └── util/ # 工具类 │ │ └── resources/ │ │ ├── mapper/ # MyBatis XML │ │ ├── application.yml # 配置文件 │ │ └── schema.sql # 数据库脚本 │ └── test/ │ └── java/ # 测试类 ├── pom.xml ├── README.md └── DESIGN.md ``` ## 📄 License MIT --- **架构师 · 严谨专业版** | 设计并实现