# 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
---
**架构师 · 严谨专业版** | 设计并实现