Files
xiaomubiao-master/前端对接指南.md
2026-05-22 16:13:20 +08:00

837 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 前端对接指南 - KMZ文件生成系统
## 📋 概述
本指南详细说明如何在前端应用中对接KMZ文件生成系统包括WebSocket消息监听、API接口调用、文件下载等功能。
## 🔗 系统架构
```
前端应用 ←→ WebSocket ←→ 后端服务 ←→ MQTT ←→ GH/YGWRXT主题
KMZ文件下载/显示
```
## 🚀 快速开始
### 1. 环境要求
- 支持WebSocket的现代浏览器
- JavaScript ES6+ 支持
- 网络连接到后端服务
### 2. 基础配置
```javascript
// 配置信息
const CONFIG = {
// WebSocket连接地址
websocketUrl: 'ws://localhost:8080/ws',
// API基础地址
apiBaseUrl: 'http://localhost:8080/api/v1',
// 工作空间ID
workspaceId: 'e3dea0f5-37f2-4d79-ae58-490af3228069',
// 用户类型
userType: 1
};
```
## 📡 WebSocket连接
### 1. 建立连接
```javascript
class WebSocketManager {
constructor(config) {
this.config = config;
this.websocket = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.reconnectInterval = 3000;
}
connect() {
try {
this.websocket = new WebSocket(this.config.websocketUrl);
this.setupEventListeners();
} catch (error) {
console.error('WebSocket连接失败:', error);
this.handleReconnect();
}
}
setupEventListeners() {
this.websocket.onopen = () => {
console.log('WebSocket连接已建立');
this.reconnectAttempts = 0;
};
this.websocket.onmessage = (event) => {
this.handleMessage(event);
};
this.websocket.onclose = () => {
console.log('WebSocket连接已关闭');
this.handleReconnect();
};
this.websocket.onerror = (error) => {
console.error('WebSocket错误:', error);
};
}
handleReconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
console.log(`尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
setTimeout(() => this.connect(), this.reconnectInterval);
} else {
console.error('WebSocket重连失败已达到最大重试次数');
}
}
}
```
### 2. 消息处理
```javascript
class MessageHandler {
constructor() {
this.handlers = new Map();
this.setupDefaultHandlers();
}
setupDefaultHandlers() {
// GK数据消息
this.handlers.set('gk_data', (data) => {
console.log('收到GK数据:', data);
this.displayGkData(data);
});
// KMZ数据消息
this.handlers.set('kmz_data', (data) => {
console.log('收到KMZ数据:', data);
this.handleKmzData(data);
});
// KMZ文件数据消息
this.handlers.set('kmz_file_data', (data) => {
console.log('收到KMZ文件数据:', data);
this.handleKmzFileData(data);
});
// KMZ生成通知
this.handlers.set('kmz_notification', (data) => {
console.log('KMZ文件生成通知:', data);
this.showNotification('success', data.message);
});
// KMZ生成错误
this.handlers.set('kmz_error', (data) => {
console.error('KMZ文件生成错误:', data);
this.showNotification('error', data.message);
});
}
handleMessage(event) {
try {
const message = JSON.parse(event.data);
const { bizCode, host } = message;
if (this.handlers.has(bizCode)) {
this.handlers.get(bizCode)(host);
} else {
console.log('未处理的消息类型:', bizCode, host);
}
} catch (error) {
console.error('消息解析失败:', error);
}
}
}
```
## 🗂️ KMZ文件处理
### 1. KMZ数据处理器
```javascript
class KmzFileHandler {
constructor() {
this.downloadedFiles = new Map();
}
// 处理KMZ数据不包含文件内容
handleKmzData(kmzData) {
console.log('KMZ文件信息:', kmzData.kmzFile);
// 显示文件信息
this.displayKmzInfo(kmzData);
// 提供下载按钮
this.createDownloadButton(kmzData);
}
// 处理KMZ文件数据包含文件内容
handleKmzFileData(kmzData) {
console.log('KMZ文件数据:', kmzData);
// 自动下载文件
this.autoDownloadKmzFile(kmzData);
// 显示文件信息
this.displayKmzInfo(kmzData);
// 解析并显示航点信息
if (kmzData.waypoints) {
this.displayWaypoints(kmzData.waypoints);
}
}
// 自动下载KMZ文件
autoDownloadKmzFile(kmzData) {
if (!kmzData.fileData) {
console.warn('KMZ数据中不包含文件内容');
return;
}
try {
// 解码Base64数据
const byteCharacters = atob(kmzData.fileData);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
const blob = new Blob([byteArray], { type: 'application/zip' });
// 创建下载链接
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = kmzData.kmzFile.fileName;
a.style.display = 'none';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
console.log('KMZ文件下载完成:', kmzData.kmzFile.fileName);
} catch (error) {
console.error('KMZ文件下载失败:', error);
}
}
// 显示KMZ文件信息
displayKmzInfo(kmzData) {
const infoContainer = document.getElementById('kmz-info');
if (!infoContainer) return;
const fileInfo = kmzData.kmzFile;
infoContainer.innerHTML = `
<div class="kmz-file-info">
<h3>KMZ文件信息</h3>
<p><strong>文件名:</strong> ${fileInfo.fileName}</p>
<p><strong>文件大小:</strong> ${this.formatFileSize(fileInfo.fileSize)}</p>
<p><strong>文件类型:</strong> ${fileInfo.fileType}</p>
<p><strong>任务ID:</strong> ${kmzData.taskId}</p>
<p><strong>设备SN:</strong> ${kmzData.deviceSn}</p>
<p><strong>生成时间:</strong> ${new Date(kmzData.timestamp).toLocaleString()}</p>
</div>
`;
}
// 显示航点信息
displayWaypoints(waypoints) {
const waypointsContainer = document.getElementById('waypoints-info');
if (!waypointsContainer) return;
let waypointsHtml = '<h3>航点信息</h3><ul>';
waypoints.forEach((waypoint, index) => {
waypointsHtml += `
<li>
航点 ${index + 1}:
经度 ${waypoint.longitude},
纬度 ${waypoint.latitude},
高度 ${waypoint.altitude}m
</li>
`;
});
waypointsHtml += '</ul>';
waypointsContainer.innerHTML = waypointsHtml;
}
// 格式化文件大小
formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// 创建下载按钮
createDownloadButton(kmzData) {
const downloadBtn = document.createElement('button');
downloadBtn.textContent = '下载KMZ文件';
downloadBtn.className = 'download-btn';
downloadBtn.onclick = () => this.downloadKmzFile(kmzData);
const container = document.getElementById('kmz-actions');
if (container) {
container.appendChild(downloadBtn);
}
}
// 下载KMZ文件
async downloadKmzFile(kmzData) {
try {
const response = await fetch(`${CONFIG.apiBaseUrl}/kmz/download/${kmzData.taskId}?fileType=${kmzData.fileType || 'dji'}`);
if (response.ok) {
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = kmzData.kmzFile.fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
} else {
throw new Error(`下载失败: ${response.statusText}`);
}
} catch (error) {
console.error('下载KMZ文件失败:', error);
this.showNotification('error', '下载失败: ' + error.message);
}
}
}
```
## 🔌 API接口调用
### 1. API客户端
```javascript
class ApiClient {
constructor(baseUrl) {
this.baseUrl = baseUrl;
}
async request(endpoint, options = {}) {
const url = `${this.baseUrl}${endpoint}`;
const defaultOptions = {
headers: {
'Content-Type': 'application/json',
},
};
const config = { ...defaultOptions, ...options };
try {
const response = await fetch(url, config);
const data = await response.json();
if (!response.ok) {
throw new Error(data.message || `HTTP ${response.status}`);
}
return data;
} catch (error) {
console.error('API请求失败:', error);
throw error;
}
}
// 获取KMZ文件信息
async getKmzFileInfo(taskId, fileType = 'dji') {
return this.request(`/kmz/info/${taskId}?fileType=${fileType}`);
}
// 获取任务KMZ文件列表
async getKmzFileList(taskId) {
return this.request(`/kmz/list/${taskId}`);
}
// 获取设备KMZ文件列表
async getDeviceKmzFileList(deviceId) {
return this.request(`/kmz/list/device/${deviceId}`);
}
// 删除KMZ文件
async deleteKmzFile(taskId, fileType = 'dji') {
return this.request(`/kmz/delete/${taskId}?fileType=${fileType}`, {
method: 'DELETE'
});
}
// 重新发送KMZ数据
async resendKmzData(taskId, fileType = 'dji') {
return this.request(`/kmz/resend/${taskId}?fileType=${fileType}`, {
method: 'POST'
});
}
// 获取统计信息
async getKmzStatistics() {
return this.request('/kmz/statistics');
}
}
```
### 2. 使用示例
```javascript
// 初始化API客户端
const apiClient = new ApiClient(CONFIG.apiBaseUrl);
// 获取KMZ文件信息
async function loadKmzFileInfo(taskId) {
try {
const result = await apiClient.getKmzFileInfo(taskId);
if (result.success) {
console.log('KMZ文件信息:', result.data);
return result.data;
}
} catch (error) {
console.error('获取KMZ文件信息失败:', error);
}
}
// 获取任务的所有KMZ文件
async function loadTaskKmzFiles(taskId) {
try {
const result = await apiClient.getKmzFileList(taskId);
if (result.success) {
console.log('任务KMZ文件列表:', result.data);
return result.data;
}
} catch (error) {
console.error('获取任务KMZ文件列表失败:', error);
}
}
```
## 🎨 用户界面组件
### 1. HTML结构
```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>KMZ文件管理系统</title>
<style>
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.status-indicator {
padding: 10px;
margin: 10px 0;
border-radius: 4px;
}
.status-connected {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.status-disconnected {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.kmz-file-info {
background-color: #f8f9fa;
padding: 15px;
border-radius: 4px;
margin: 10px 0;
}
.waypoints-info {
background-color: #e9ecef;
padding: 15px;
border-radius: 4px;
margin: 10px 0;
}
.download-btn {
background-color: #007bff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
}
.download-btn:hover {
background-color: #0056b3;
}
.notification {
position: fixed;
top: 20px;
right: 20px;
padding: 15px;
border-radius: 4px;
color: white;
z-index: 1000;
}
.notification.success {
background-color: #28a745;
}
.notification.error {
background-color: #dc3545;
}
</style>
</head>
<body>
<div class="container">
<h1>KMZ文件管理系统</h1>
<!-- 连接状态 -->
<div id="connection-status" class="status-indicator status-disconnected">
WebSocket未连接
</div>
<!-- KMZ文件信息 -->
<div id="kmz-info"></div>
<!-- 航点信息 -->
<div id="waypoints-info"></div>
<!-- 操作按钮 -->
<div id="kmz-actions"></div>
<!-- 日志区域 -->
<div id="log-area">
<h3>系统日志</h3>
<div id="log-content" style="height: 300px; overflow-y: auto; border: 1px solid #ccc; padding: 10px;"></div>
</div>
</div>
<script src="websocket-manager.js"></script>
<script src="message-handler.js"></script>
<script src="kmz-file-handler.js"></script>
<script src="api-client.js"></script>
<script src="app.js"></script>
</body>
</html>
```
### 2. 主应用逻辑
```javascript
// app.js
class KmzApp {
constructor() {
this.websocketManager = new WebSocketManager(CONFIG);
this.messageHandler = new MessageHandler();
this.kmzFileHandler = new KmzFileHandler();
this.apiClient = new ApiClient(CONFIG.apiBaseUrl);
this.init();
}
init() {
// 设置消息处理器
this.websocketManager.onMessage = (data) => {
this.messageHandler.handleMessage(data);
};
// 连接WebSocket
this.websocketManager.connect();
// 更新连接状态
this.updateConnectionStatus();
// 设置定时器更新状态
setInterval(() => {
this.updateConnectionStatus();
}, 1000);
}
updateConnectionStatus() {
const statusElement = document.getElementById('connection-status');
if (this.websocketManager.websocket &&
this.websocketManager.websocket.readyState === WebSocket.OPEN) {
statusElement.textContent = 'WebSocket已连接';
statusElement.className = 'status-indicator status-connected';
} else {
statusElement.textContent = 'WebSocket未连接';
statusElement.className = 'status-indicator status-disconnected';
}
}
// 显示通知
showNotification(type, message) {
const notification = document.createElement('div');
notification.className = `notification ${type}`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
document.body.removeChild(notification);
}, 3000);
}
// 添加日志
addLog(message) {
const logContent = document.getElementById('log-content');
const timestamp = new Date().toLocaleTimeString();
const logEntry = document.createElement('div');
logEntry.textContent = `[${timestamp}] ${message}`;
logContent.appendChild(logEntry);
logContent.scrollTop = logContent.scrollHeight;
}
}
// 启动应用
const app = new KmzApp();
```
## 📊 数据格式说明
### 1. WebSocket消息格式
```javascript
// GK数据消息
{
"bizCode": "gk_data",
"host": {
"sid": "1581F6GKB244J00402NM",
"tm": 1706342400000,
"task_id": "TASK_001",
"task_name": "巡检任务",
"task_type": 1,
"route": [
{
"longitude": 112.894686,
"latitude": 28.207833,
"altitude": 50.0
}
]
}
}
// KMZ文件数据消息
{
"bizCode": "kmz_file_data",
"host": {
"messageType": "kmz_file_data",
"taskId": "TASK_001",
"taskName": "巡检任务",
"deviceSn": "1581F6GKB244J00402NM",
"dataType": "route",
"timestamp": 1706342400000,
"kmzFile": {
"fileName": "巡检任务_1581F6GKB244J00402NM_dji_20250127_143022.kmz",
"fileSize": 3891,
"filePath": "/opt/dji/routes/巡检任务_1581F6GKB244J00402NM_dji_20250127_143022.kmz",
"downloadUrl": "/api/v1/kmz/download/TASK_001",
"fileType": "kmz"
},
"fileData": "UEsDBBQAAAAIAA...", // Base64编码的文件内容
"waypoints": [
{
"longitude": 112.894686,
"latitude": 28.207833,
"altitude": 50.0
}
]
}
}
```
### 2. API响应格式
```javascript
// 成功响应
{
"success": true,
"data": {
// 具体数据
},
"message": "操作成功"
}
// 错误响应
{
"success": false,
"message": "错误信息",
"code": "ERROR_CODE"
}
```
## 🔧 配置选项
### 1. 环境配置
```javascript
const ENV_CONFIG = {
development: {
websocketUrl: 'ws://localhost:8080/ws',
apiBaseUrl: 'http://localhost:8080/api/v1',
workspaceId: 'e3dea0f5-37f2-4d79-ae58-490af3228069'
},
production: {
websocketUrl: 'wss://your-domain.com/ws',
apiBaseUrl: 'https://your-domain.com/api/v1',
workspaceId: 'your-workspace-id'
}
};
```
### 2. 功能配置
```javascript
const FEATURE_CONFIG = {
// 是否自动下载KMZ文件
autoDownload: true,
// 是否显示详细日志
verboseLogging: true,
// 重连配置
reconnect: {
maxAttempts: 5,
interval: 3000
},
// 文件大小限制(字节)
maxFileSize: 10 * 1024 * 1024 // 10MB
};
```
## 🚨 错误处理
### 1. 常见错误类型
```javascript
class ErrorHandler {
static handleWebSocketError(error) {
console.error('WebSocket错误:', error);
// 处理WebSocket连接错误
}
static handleApiError(error) {
console.error('API错误:', error);
// 处理API调用错误
}
static handleFileError(error) {
console.error('文件处理错误:', error);
// 处理文件下载/解析错误
}
}
```
### 2. 错误恢复策略
```javascript
class ErrorRecovery {
static retryOperation(operation, maxRetries = 3) {
return new Promise((resolve, reject) => {
let attempts = 0;
const tryOperation = () => {
attempts++;
operation()
.then(resolve)
.catch(error => {
if (attempts < maxRetries) {
console.log(`操作失败,重试中 (${attempts}/${maxRetries})`);
setTimeout(tryOperation, 1000 * attempts);
} else {
reject(error);
}
});
};
tryOperation();
});
}
}
```
## 📝 最佳实践
### 1. 性能优化
- 使用防抖和节流处理频繁的消息
- 合理使用缓存避免重复请求
- 及时清理不需要的资源
### 2. 用户体验
- 提供清晰的状态指示
- 显示操作进度
- 提供错误提示和恢复建议
### 3. 安全性
- 验证所有输入数据
- 使用HTTPS/WSS连接
- 避免在客户端存储敏感信息
## 🔍 调试指南
### 1. 开启调试模式
```javascript
const DEBUG = true;
if (DEBUG) {
// 开启详细日志
console.log('调试模式已开启');
// 监听所有WebSocket消息
const originalOnMessage = WebSocket.prototype.onmessage;
WebSocket.prototype.onmessage = function(event) {
console.log('WebSocket消息:', event.data);
originalOnMessage.call(this, event);
};
}
```
### 2. 常见问题排查
1. **WebSocket连接失败**
- 检查URL是否正确
- 确认服务器是否运行
- 检查网络连接
2. **KMZ文件下载失败**
- 检查文件是否存在
- 验证API接口是否正常
- 确认文件权限
3. **消息处理异常**
- 检查消息格式是否正确
- 验证消息处理器是否注册
- 查看控制台错误信息
## 📞 技术支持
如有问题,请联系开发团队或查看相关文档:
- API文档`/api/v1/kmz/docs`
- 系统日志:检查浏览器控制台和后端日志
- 问题反馈提交Issue到项目仓库
---
**注意**: 本指南基于当前系统实现,如有更新请及时同步文档。