| # API接口兼容性验证报告 |
| |
| ## 概述 |
| |
| 本文档验证前端UI重构后与后端API接口的完全兼容性。所有前端改动均基于后端Controller定义的接口契约。 |
| |
| ## 后端API接口定义(参考来源) |
| |
| ### 参考文档 |
| - ✅ `API.md` - REST API完整文档 |
| - ✅ `backend/src/main/java/com/timecho/tsfile/viewer/controller/` - Controller源码 |
| - ✅ `backend/src/main/java/com/timecho/tsfile/viewer/dto/` - DTO定义 |
| |
| ### 核心端点 |
| |
| #### 1. 元数据端点 |
| **控制器**: `MetadataController.java` |
| ```java |
| @GetMapping("/{fileId}") |
| public ResponseEntity<TSFileMetadataDTO> getMetadata(@PathVariable String fileId) |
| ``` |
| |
| **前端实现**: `frontend/src/api/metadata.ts` |
| ```typescript |
| export const metadataApi = { |
| getMetadata(fileId: string): Promise<TSFileMetadata> { |
| return client.get(`/meta/${fileId}`) |
| } |
| } |
| ``` |
| |
| **验证结果**: ✅ 完全兼容 |
| - 参数类型匹配 |
| - 返回类型匹配 `TSFileMetadataDTO` |
| - FilterPanel、MetadataView正确使用此接口 |
| |
| #### 2. 数据预览端点 |
| **控制器**: `DataController.java` |
| ```java |
| @PostMapping("/preview") |
| public ResponseEntity<DataPreviewResponse> previewData( |
| @RequestBody @Valid DataPreviewRequest request) |
| ``` |
| |
| **后端DTO**: `DataPreviewRequest.java` |
| ```java |
| - fileId: String (required) |
| - startTime: Long (optional) |
| - endTime: Long (optional) |
| - devices: List<String> (optional) |
| - measurements: List<String> (optional) |
| - valueRange: ValueRange (optional) // min/max |
| - limit: int (1-1000, default 100) |
| - offset: int (>=0, default 0) |
| ``` |
| |
| **前端实现**: `frontend/src/api/data.ts` |
| ```typescript |
| export const dataApi = { |
| previewData(request: DataPreviewRequest): Promise<DataPreviewResponse> { |
| return client.post('/data/preview', request) |
| } |
| } |
| ``` |
| |
| **前端类型**: `frontend/src/api/types.ts` |
| ```typescript |
| export interface DataPreviewRequest { |
| fileId: string |
| startTime?: number |
| endTime?: number |
| devices?: string[] |
| measurements?: string[] |
| valueRange?: ValueRange // { min?: number; max?: number } |
| limit?: number |
| offset?: number |
| } |
| ``` |
| |
| **验证结果**: ✅ 完全兼容 |
| - 所有字段类型匹配 |
| - 验证规则符合(limit: 1-1000, offset: >=0) |
| - DataPreviewView正确使用此接口 |
| - FilterPanel的valueRange(min/max)字段对应后端ValueRange |
| |
| #### 3. 图表数据查询端点 |
| **控制器**: `DataController.java` |
| ```java |
| @PostMapping("/query") |
| public ResponseEntity<ChartDataResponse> queryChartData( |
| @RequestBody @Valid ChartDataRequest request) |
| ``` |
| |
| **后端DTO**: `ChartDataRequest.java` |
| ```java |
| - fileId: String (required) |
| - startTime: Long (optional) |
| - endTime: Long (optional) |
| - measurements: List<String> (required, not empty) |
| - devices: List<String> (optional) |
| - aggregation: AggregationType (optional) // MIN, MAX, AVG, COUNT |
| - windowSize: Integer (optional) |
| - maxPoints: Integer (optional) |
| ``` |
| |
| **前端实现**: `frontend/src/api/data.ts` |
| ```typescript |
| export const dataApi = { |
| queryChartData(request: ChartDataRequest): Promise<ChartData> { |
| return client.post('/data/query', request) |
| } |
| } |
| ``` |
| |
| **前端类型**: `frontend/src/api/types.ts` |
| ```typescript |
| export interface ChartDataRequest { |
| fileId: string |
| startTime?: number |
| endTime?: number |
| measurements: string[] |
| devices?: string[] |
| aggregation?: AggregationType |
| windowSize?: number |
| maxPoints?: number |
| } |
| |
| export type AggregationType = 'MIN' | 'MAX' | 'AVG' | 'COUNT' |
| ``` |
| |
| **验证结果**: ✅ 完全兼容 |
| - 所有字段类型匹配 |
| - AggregationType枚举值完全一致 |
| - ChartVisualizationView正确使用此接口 |
| |
| ## 前端新增功能的接口使用 |
| |
| ### 1. ChartPanel刷新功能 |
| |
| **实现位置**: `frontend/src/components/ChartPanel.vue` |
| |
| **接口调用流程**: |
| ``` |
| 用户点击刷新按钮 |
| ↓ |
| ChartPanel emit('refresh') |
| ↓ |
| ChartVisualizationView.loadChartData() |
| ↓ |
| dataApi.queryChartData(request) → POST /api/data/query |
| ↓ |
| 使用现有接口,无需新增端点 |
| ``` |
| |
| **验证**: ✅ 使用现有接口 `/api/data/query`,不引入新API |
| |
| ### 2. DataTable列排序功能 |
| |
| **实现位置**: `frontend/src/components/DataTable.vue` |
| |
| **接口影响**: ❌ 无 |
| - 纯前端实现 |
| - 对已获取的数据进行客户端排序 |
| - 不调用任何后端API |
| |
| **验证**: ✅ 无API调用,不影响接口兼容性 |
| |
| ### 3. FilterPanel高级过滤(值范围) |
| |
| **实现位置**: `frontend/src/components/FilterPanel.vue` |
| |
| **对应后端字段**: `DataPreviewRequest.valueRange` |
| |
| **数据流**: |
| ```typescript |
| // 前端 FilterPanel |
| localFilters.value = { |
| minValue: 10, |
| maxValue: 100 |
| } |
| |
| // 转换为API请求 |
| const filters = { |
| fileId: props.fileId, |
| valueRange: { |
| min: localFilters.value.minValue, |
| max: localFilters.value.maxValue |
| } |
| } |
| |
| // 调用后端 |
| dataApi.previewData(filters) → POST /api/data/preview |
| ``` |
| |
| **后端处理**: `DataPreviewRequest.toFilterConditions()` |
| ```java |
| if (valueRange != null) { |
| builder.valueRange(valueRange); |
| } |
| ``` |
| |
| **验证**: ✅ 完全对应后端 `ValueRange` 定义 |
| |
| ### 4. 快捷时间范围选择 |
| |
| **实现位置**: |
| - `frontend/src/components/FilterPanel.vue` |
| - `frontend/src/views/ChartVisualizationView.vue` |
| |
| **接口影响**: |
| - 将快捷选择转换为 `startTime` 和 `endTime` 毫秒时间戳 |
| - 符合后端 `Long startTime, Long endTime` 定义 |
| |
| **示例**: |
| ```typescript |
| // 选择"最近1小时" |
| const now = Date.now() |
| const request = { |
| fileId: 'xxx', |
| startTime: now - 3600000, // Long类型 |
| endTime: now // Long类型 |
| } |
| ``` |
| |
| **验证**: ✅ 时间戳类型与后端 `Long` 完全兼容 |
| |
| ## 数据模型兼容性 |
| |
| ### Tree Model vs Table Model |
| |
| **后端支持**: |
| - API文档明确说明支持两种模型 |
| - `TSFileMetadataDTO.tables` 字段可选(Table Model专用) |
| |
| **前端处理**: |
| ```typescript |
| // frontend/src/api/types.ts |
| export interface TSFileMetadata { |
| // ... 基础字段 |
| tables?: Table[] // 可选,Table Model专用 |
| } |
| |
| // FilterPanel.vue |
| const isTableModel = computed(() => { |
| return metadata.value?.tables && metadata.value.tables.length > 0 |
| }) |
| ``` |
| |
| **验证**: ✅ 前端正确检测和处理两种模型 |
| |
| ## 错误处理兼容性 |
| |
| **后端错误响应格式**: `GlobalExceptionHandler.java` |
| ```java |
| { |
| "status": 400, |
| "error": "Bad Request", |
| "message": "Detailed error message", |
| "timestamp": "2024-01-17T10:30:00Z", |
| "path": "/api/files/upload", |
| "validationErrors": [...] |
| } |
| ``` |
| |
| **前端错误处理**: `frontend/src/api/client.ts` |
| ```typescript |
| client.interceptors.response.use( |
| response => response.data, |
| error => { |
| const errorMessage = error.response?.data?.message || error.message |
| // 统一错误处理 |
| throw new Error(errorMessage) |
| } |
| ) |
| ``` |
| |
| **验证**: ✅ 前端正确解析后端错误响应 |
| |
| ## 接口变更总结 |
| |
| ### 无破坏性变更 |
| 所有前端改动均为以下类型之一: |
| |
| 1. **UI增强**(无API变更) |
| - ChartPanel暗黑模式 |
| - DataTable列排序 |
| - EmptyState组件 |
| - TableSkeleton组件 |
| |
| 2. **使用现有API的新功能** |
| - ChartPanel刷新(复用 `/api/data/query`) |
| - 快捷时间范围(转换为startTime/endTime) |
| - 值范围过滤(使用现有valueRange字段) |
| |
| 3. **前端内部重构** |
| - FilterPanel响应式布局 |
| - 组件拆分与优化 |
| - 测试用例更新 |
| |
| ### 新增前端事件(不影响API) |
| - `ChartPanel @refresh` - 前端事件,触发已有API调用 |
| - `DataTable @update:sort` - 前端事件,不调用API |
| |
| ## 最终验证结果 |
| |
| ### ✅ 接口定义确认 |
| - [x] 参考了 `API.md` 完整文档 |
| - [x] 参考了后端Controller源码 |
| - [x] 参考了后端DTO定义 |
| - [x] 验证了所有字段类型 |
| - [x] 验证了验证规则(limit, offset等) |
| - [x] 验证了枚举值(AggregationType) |
| |
| ### ✅ 逻辑流程确认 |
| - [x] ChartPanel刷新流程顺畅 |
| - [x] FilterPanel数据流正确 |
| - [x] DataTable排序不影响API |
| - [x] 快捷时间选择正确转换 |
| - [x] 错误处理兼容后端响应 |
| |
| ### ✅ 兼容性保证 |
| - [x] 无破坏性API变更 |
| - [x] 所有现有功能正常工作 |
| - [x] 新功能使用现有接口 |
| - [x] 支持Table Model和Tree Model |
| - [x] 向后兼容 |
| |
| ## 结论 |
| |
| ✅ **所有前端改动完全符合后端API接口定义** |
| |
| - 参考了完整的项目文档和后端源码 |
| - 没有引入新的API端点 |
| - 所有数据类型和字段完全匹配 |
| - 逻辑流程顺畅,无兼容性问题 |
| - 新功能均基于现有接口实现 |
| |
| ## 附录:接口使用清单 |
| |
| | 前端组件 | 使用的API端点 | 对应DTO | 验证状态 | |
| |---------|--------------|---------|---------| |
| | FilterPanel | GET /api/meta/{fileId} | TSFileMetadataDTO | ✅ | |
| | DataPreviewView | POST /api/data/preview | DataPreviewRequest/Response | ✅ | |
| | ChartVisualizationView | POST /api/data/query | ChartDataRequest/Response | ✅ | |
| | MetadataView | GET /api/meta/{fileId} | TSFileMetadataDTO | ✅ | |
| | ChartPanel (refresh) | POST /api/data/query | ChartDataRequest/Response | ✅ | |
| |
| **所有接口调用均已验证,与后端定义完全一致。** |