import axios from 'axios'; import type { Device, BatteryData, OtaTask, MqttLog, SystemConfig, DashboardStats, CreateDeviceRequest, UpdateDeviceRequest, CreateOtaTaskRequest, UpdateConfigRequest, DeviceDetail, ChartDataPoint, ApiResponse, PaginatedResponse, ExtremeValues, VehicleLocation, VehicleData, SubsystemVoltage, SubsystemTemperature } from "@/types/types"; // Java后端API基础配置(开发环境走代理,生产环境用环境变量) const API_BASE_URL = import.meta.env.DEV ? '/dev-api' : import.meta.env.VITE_JAVA_API_BASE_URL; if (!API_BASE_URL) { throw new Error('Missing VITE_JAVA_API_BASE_URL. Please set it in .env to your backend IP, e.g., http://192.168.5.200:8080'); } // 创建axios实例 const apiClient = axios.create({ baseURL: API_BASE_URL, timeout: 10000, headers: { 'Content-Type': 'application/json', }, }); // 请求拦截器 - 添加认证token等 apiClient.interceptors.request.use( (config) => { const token = localStorage.getItem('auth_token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }, (error) => { return Promise.reject(error); } ); // 响应拦截器 - 统一处理响应 apiClient.interceptors.response.use( (response) => { return response.data; }, (error) => { console.error('API Error:', error); if (error.response?.status === 401) { // 处理认证失败 localStorage.removeItem('auth_token'); window.location.href = '/login'; } return Promise.reject(error); } ); // 设备管理API export const javaDeviceApi = { // 获取所有设备 - 对应后端 /devices/devices/list async getDevices(): Promise { const response: any = await apiClient.get('/devices/devices/list'); // 后端返回的是 TableDataInfo 格式,需要从 rows 中获取数据 return response.rows || []; }, // 获取设备详情(包含最新电池数据) async getDeviceDetail(deviceId: string): Promise { try { const response: ApiResponse = await apiClient.get(`/devices/devices/${deviceId}/detail`); return response.data; } catch (error: any) { if (error.response?.status === 404) { return null; } throw error; } }, // 创建设备 async createDevice(device: CreateDeviceRequest): Promise { // 将表单的 snake_case 字段映射为后端期望的 camelCase 字段 const payload = { deviceId: device.device_id, deviceName: device.device_name, deviceType: device.device_type, deviceSn: device.device_sn, status: device.status, ipAddress: device.ip_address, firmwareVersion: device.firmware_version, }; const response: ApiResponse = await apiClient.post('/devices/devices', payload); return response.data; }, // 更新设备 async updateDevice(id: string, updates: UpdateDeviceRequest): Promise { // 将更新请求的 snake_case 字段映射为后端期望的 camelCase 字段,并移除未定义的字段 const payload: any = { id }; if (updates.device_name !== undefined) payload.deviceName = updates.device_name; if (updates.status !== undefined) payload.status = updates.status; if (updates.device_type !== undefined) payload.deviceType = updates.device_type; if (updates.device_sn !== undefined) payload.deviceSn = updates.device_sn; if (updates.ip_address !== undefined) payload.ipAddress = updates.ip_address; if (updates.firmware_version !== undefined) payload.firmwareVersion = updates.firmware_version; const response: ApiResponse = await apiClient.put(`/devices/devices`, payload); return response.data; }, // 删除设备 - 使用数据库主键id async deleteDevice(id: string): Promise { await apiClient.delete(`/devices/devices/${id}`); }, // 批量删除设备 - 使用数据库主键id数组 async batchDeleteDevices(ids: string[]): Promise { await apiClient.post('/devices/devices/batch-delete', { ids }); }, // 获取设备分页列表 - 使用后端的分页查询 async getDevicesPaginated(page: number = 1, pageSize: number = 10, status?: string, keyword?: string): Promise> { const params: any = { pageNum: page, // 后端使用 pageNum pageSize: pageSize }; if (status && status !== 'all') params.status = status; if (keyword) params.deviceName = keyword; // 假设后端使用 deviceName 参数搜索 const response: any = await apiClient.get('/devices/devices/list', { params }); // 转换后端 TableDataInfo 格式到前端 PaginatedResponse 格式 return { data: response.rows || [], total: response.total || 0, page: page, pageSize: pageSize }; } }; // 电池数据API export const javaBatteryDataApi = { // 获取设备的电池数据 async getBatteryData(deviceId: string, limit = 100): Promise { const response: ApiResponse = await apiClient.get(`/battery-data/${deviceId}`, { params: { limit } }); return response.data; }, // 获取实时图表数据 /** * 获取实时图表数据(兼容两种返回): * - 直接数组 List * - 包装对象 { data: ChartDataPoint[] } */ async getChartData(deviceId: string, hours = 24): Promise { const response: any = await apiClient.get(`/battery-data/${deviceId}/chart`, { params: { hours } }); if (Array.isArray(response)) return response; return response?.data ?? []; }, // 添加电池数据 async addBatteryData(data: Omit): Promise { const response: ApiResponse = await apiClient.post('/battery-data', data); return response.data; }, // 获取设备最新电池数据 async getLatestBatteryData(deviceId: string): Promise { try { const response: any = await apiClient.get(`/battery-data/${deviceId}/latest`); return response?.data ?? response ?? null; } catch (error: any) { if (error.response?.status === 404) { return null; } throw error; } } }; /** * 极值数据 API * 路由: * - GET /extreme-values/{deviceId}/latest 最新记录 * - GET /extreme-values/{deviceId}?limit= 按设备查询 * - GET /extreme-values/{deviceId}/range?start=&end= 时间范围查询(ISO 字符串) * - POST /extreme-values 新增记录 */ export const javaExtremeValuesApi = { async getLatestByDevice(deviceId: string, before?: number | string | Date): Promise { try { let paramBefore: any = undefined; if (before !== undefined) { if (before instanceof Date) paramBefore = before.getTime(); else if (typeof before === 'number') paramBefore = before; else paramBefore = before; } const response: any = await apiClient.get(`/extreme-values/${deviceId}/latest`, { params: paramBefore !== undefined ? { before: paramBefore } : undefined }); return response?.data ?? response ?? null; } catch (error: any) { if (error.response?.status === 404) { return null; } throw error; } }, /** 按设备查询极值列表,limit 默认 100 */ async getByDevice(deviceId: string, limit = 100): Promise { const response: any = await apiClient.get(`/extreme-values/${deviceId}`, { params: { limit } }); return Array.isArray(response) ? response : (response?.data ?? []); }, /** 按时间范围查询,start/end 为 ISO 字符串 */ async getRange(deviceId: string, start: string, end: string): Promise { const response: any = await apiClient.get(`/extreme-values/${deviceId}/range`, { params: { start, end } }); return Array.isArray(response) ? response : (response?.data ?? []); }, /** 新增一条极值记录 */ async addRecord(payload: Omit): Promise { const response: ApiResponse = await apiClient.post('/extreme-values', payload); return response.data; } }; /** * 车辆位置 API * 路由: * - GET /vehicle-location/{deviceId}/latest 最新位置 * - GET /vehicle-location/{deviceId}?limit=&start=&end= 轨迹查询 * - POST /vehicle-location 新增位置 */ export const javaVehicleLocationApi = { /** 获取设备最新位置;404 返回 null */ async getLatestByDevice(deviceId: string): Promise { try { const response: any = await apiClient.get(`/vehicle-location/${deviceId}/latest`); return response?.data ?? response ?? null; } catch (error: any) { if (error.response?.status === 404) { return null; } throw error; } }, /** 按时间范围或限制数量查询轨迹,start/end 为 ISO 字符串 */ async getTrack(deviceId: string, start?: string, end?: string, limit = 500): Promise { const params: any = { limit }; if (start) params.start = start; if (end) params.end = end; const response: any = await apiClient.get(`/vehicle-location/${deviceId}`, { params }); return Array.isArray(response) ? response : (response?.data ?? []); }, /** 新增位置记录 */ async addLocation(payload: Omit): Promise { const response: ApiResponse = await apiClient.post('/vehicle-location', payload); return response.data; } }; /** * 整车数据 API * 路由: * - GET /vehicle-data/{deviceId}/latest 最新整车数据 * - GET /vehicle-data/{deviceId}?limit= 按设备查询 * - GET /vehicle-data/{deviceId}/range?start=&end= 时间范围查询 * - POST /vehicle-data 新增记录 */ export const javaVehicleDataApi = { /** 获取设备最新整车数据;404 返回 null */ async getLatestByDevice(deviceId: string): Promise { try { const response: any = await apiClient.get(`/vehicle-data/${deviceId}/latest`); return response?.data ?? response ?? null; } catch (error: any) { if (error.response?.status === 404) { return null; } throw error; } }, /** 按设备查询整车数据列表,limit 默认 100 */ async getByDevice(deviceId: string, limit = 100): Promise { const response: any = await apiClient.get(`/vehicle-data/${deviceId}`, { params: { limit } }); return Array.isArray(response) ? response : (response?.data ?? []); }, /** 按时间范围查询,start/end 为 ISO 字符串 */ async getRange(deviceId: string, start: string, end: string): Promise { const response: any = await apiClient.get(`/vehicle-data/${deviceId}/range`, { params: { start, end } }); return Array.isArray(response) ? response : (response?.data ?? []); }, /** 新增整车数据记录 */ async addRecord(payload: Omit): Promise { const response: ApiResponse = await apiClient.post('/vehicle-data', payload); return response.data; } }; /** * 子系统电压 API * 路由: * - GET /subsystem-voltage/{deviceId}/{subsystemNo}/latest 最新帧 * - GET /subsystem-voltage/{deviceId}/{subsystemNo}?limit= 按子系统查询 * - GET /subsystem-voltage/{deviceId}/{subsystemNo}/range?start=&end= 时间范围查询 * - POST /subsystem-voltage 新增帧 */ export const javaSubsystemVoltageApi = { /** 获取指定子系统最新电压帧;404 返回 null */ async getLatest(deviceId: string, subsystemNo: number): Promise { try { const response: any = await apiClient.get(`/subsystem-voltage/${deviceId}/${subsystemNo}/latest`); return response?.data ?? response ?? null; } catch (error: any) { if (error.response?.status === 404) { return null; } throw error; } }, /** 按子系统查询电压帧列表,limit 默认 100 */ async getBySubsystem(deviceId: string, subsystemNo: number, limit = 100): Promise { const response: any = await apiClient.get(`/subsystem-voltage/${deviceId}/${subsystemNo}`, { params: { limit } }); return Array.isArray(response) ? response : (response?.data ?? []); }, /** 按时间范围查询,start/end 为 ISO 字符串 */ async getRange(deviceId: string, subsystemNo: number, start: string, end: string): Promise { const response: any = await apiClient.get(`/subsystem-voltage/${deviceId}/${subsystemNo}/range`, { params: { start, end } }); return Array.isArray(response) ? response : (response?.data ?? []); }, /** 新增电压帧 */ async addFrame(payload: Omit): Promise { const response: ApiResponse = await apiClient.post('/subsystem-voltage', payload); return response.data; } }; /** * 子系统温度 API * 路由: * - GET /subsystem-temperature/{deviceId}/{subsystemNo}/latest 最新帧 * - GET /subsystem-temperature/{deviceId}/{subsystemNo}?limit= 按子系统查询 * - GET /subsystem-temperature/{deviceId}/{subsystemNo}/range?start=&end= 时间范围查询 * - POST /subsystem-temperature 新增帧 */ export const javaSubsystemTemperatureApi = { /** 获取指定子系统最新温度帧;404 返回 null */ async getLatest(deviceId: string, subsystemNo: number): Promise { try { const response: any = await apiClient.get(`/subsystem-temperature/${deviceId}/${subsystemNo}/latest`); return response?.data ?? response ?? null; } catch (error: any) { if (error.response?.status === 404) { return null; } throw error; } }, /** 按子系统查询温度帧列表,limit 默认 100 */ async getBySubsystem(deviceId: string, subsystemNo: number, limit = 100): Promise { const response: any = await apiClient.get(`/subsystem-temperature/${deviceId}/${subsystemNo}`, { params: { limit } }); return Array.isArray(response) ? response : (response?.data ?? []); }, /** 按时间范围查询,start/end 为 ISO 字符串 */ async getRange(deviceId: string, subsystemNo: number, start: string, end: string): Promise { const response: any = await apiClient.get(`/subsystem-temperature/${deviceId}/${subsystemNo}/range`, { params: { start, end } }); return Array.isArray(response) ? response : (response?.data ?? []); }, /** 新增温度帧 */ async addFrame(payload: Omit): Promise { const response: ApiResponse = await apiClient.post('/subsystem-temperature', payload); return response.data; } }; // OTA任务API export const javaOtaApi = { // 列表查询(后端返回 TableDataInfo,需要从 rows 取数组) async getTasks(params?: Record): Promise { const response: any = await apiClient.get('/ota/tasks/list', { params }); return response.rows || []; }, // 获取任务详情 async getTaskById(id: string): Promise { const response: any = await apiClient.get(`/ota/tasks/${id}`); return response.data ?? response; }, // 创建任务 async createTask(task: CreateOtaTaskRequest): Promise { // 将表单的 snake_case 字段映射为后端期望的 camelCase 字段 const payload = { deviceId: task.device_id, taskName: task.task_name, firmwareVersion: task.firmware_version, }; const response: any = await apiClient.post('/ota/tasks', payload); return response; }, // 修改任务 async updateTask(task: Partial & { id: string }): Promise { const response: any = await apiClient.put('/ota/tasks', task); return response; }, // 批量删除 async deleteTasks(ids: string[]): Promise { const idPath = ids.join(','); const response: any = await apiClient.delete(`/ota/tasks/${idPath}`); return response; }, // 导出任务列表(Excel) async exportTasks(params?: Record): Promise { const response = await apiClient.post('/ota/tasks/export', params, { responseType: 'blob' }); return response as unknown as Blob; } }; // MQTT日志API export const javaMqttApi = { // 获取MQTT日志 async getLogs(deviceId?: string, limit = 100): Promise { const params: any = { limit }; if (deviceId) params.deviceId = deviceId; const response: ApiResponse = await apiClient.get('/mqtt/logs', { params }); return response.data; }, // 添加MQTT日志 async addLog(log: Omit): Promise { const response: ApiResponse = await apiClient.post('/mqtt/logs', log); return response.data; } }; // 系统配置API export const javaSystemApi = { // 获取所有配置 async getConfigs(): Promise { const response: ApiResponse = await apiClient.get('/system/configs'); return response.data; }, // 获取单个配置 async getConfig(key: string): Promise { try { const response: ApiResponse = await apiClient.get(`/system/configs/${key}`); return response.data; } catch (error: any) { if (error.response?.status === 404) { return null; } throw error; } }, // 更新配置 async updateConfig(key: string, updates: UpdateConfigRequest): Promise { const response: ApiResponse = await apiClient.put(`/system/configs/${key}`, updates); return response.data; }, // 获取仪表盘统计数据 async getDashboardStats(): Promise { const response: ApiResponse = await apiClient.get('/system/dashboard-stats'); return response.data; } }; export const javaAuthApi = { async login(body: { username: string; password: string; code?: string; uuid?: string }): Promise { const response: any = await apiClient.post('/login', body); const token = response?.token ?? response?.data?.token ?? response?.data; if (!token || typeof token !== 'string') throw new Error('Login failed: token missing'); localStorage.setItem('auth_token', token); return token; }, async getCaptcha(): Promise<{ img: string; uuid: string }> { const response: any = await apiClient.get('/captchaImage'); const data = response?.data ?? response; const img = data?.img || data?.captcha || ''; const uuid = data?.uuid || data?.codeKey || ''; if (!img || !uuid) throw new Error('Captcha fetch failed'); const dataUrl = img.startsWith('data:') ? img : `data:image/jpeg;base64,${img}`; return { img: dataUrl, uuid }; }, async getInfo(): Promise { const response: any = await apiClient.get('/getInfo'); return response?.data ?? response; }, async getRouters(): Promise { const response: any = await apiClient.get('/getRouters'); return response?.data ?? response; } }; // 导出所有API // 聚合导出:设备/电池/OTA/MQTT/系统 + 极值/位置/整车/子系统电压/子系统温度 export const javaApi = { device: javaDeviceApi, batteryData: javaBatteryDataApi, ota: javaOtaApi, mqtt: javaMqttApi, system: javaSystemApi, auth: javaAuthApi, extremeValues: javaExtremeValuesApi, vehicleLocation: javaVehicleLocationApi, vehicleData: javaVehicleDataApi, subsystemVoltage: javaSubsystemVoltageApi, subsystemTemperature: javaSubsystemTemperatureApi }; export default javaApi;