import api from './index'; const cacheManager = (() => { const cache = new Map(); const cacheTimestamps = new Map(); const defaultTTL = 5 * 60 * 1000; const config = new Map(); const generateKey = (method, url, params) => { const paramsStr = params ? JSON.stringify(params, Object.keys(params).sort()) : ''; return `${method}:${url}:${paramsStr}`; }; const isExpired = key => { const timestamp = cacheTimestamps.get(key); if (!timestamp) return true; const ttl = config.get(key)?.ttl || defaultTTL; return Date.now() - timestamp > ttl; }; const get = (method, url, params) => { const key = generateKey(method, url, params); if (isExpired(key)) { cache.delete(key); cacheTimestamps.delete(key); return null; } return cache.get(key); }; const set = (method, url, params, data, ttl) => { const key = generateKey(method, url, params); cache.set(key, data); cacheTimestamps.set(key, Date.now()); config.set(key, { ttl }); return key; }; const invalidate = url => { const keysToDelete = []; cache.forEach((_, key) => { if (key.includes(url)) { keysToDelete.push(key); } }); keysToDelete.forEach(key => { cache.delete(key); cacheTimestamps.delete(key); config.delete(key); }); return keysToDelete.length; }; const invalidatePattern = pattern => { const regex = new RegExp(pattern); const keysToDelete = []; cache.forEach((_, key) => { if (regex.test(key)) { keysToDelete.push(key); } }); keysToDelete.forEach(key => { cache.delete(key); cacheTimestamps.delete(key); config.delete(key); }); return keysToDelete.length; }; const clear = () => { cache.clear(); cacheTimestamps.clear(); config.clear(); }; const setTTL = (url, ttl) => { config.set(url, { ttl }); }; const getStats = () => { return { size: cache.size, keys: Array.from(cache.keys()), }; }; return { get, set, invalidate, invalidatePattern, clear, setTTL, getStats, defaultTTL, }; })(); const cacheInterceptor = api => { const requestCache = new Set(); const pendingRequests = new Map(); api.interceptors.request.use( config => { if (config.method?.toLowerCase() === 'get') { const cacheKey = cacheManager.generateKey(config.method, config.url, config.params); if (requestCache.has(cacheKey)) { config.adapter = () => { const cachedData = cacheManager.get(config.method, config.url, config.params); if (cachedData) { return Promise.resolve({ data: cachedData, status: 200, statusText: 'OK', headers: {}, config, }); } requestCache.delete(cacheKey); return api.request(config); }; } } return config; }, error => Promise.reject(error) ); api.interceptors.response.use( response => { if (response.config.method?.toLowerCase() === 'get') { const cacheKey = cacheManager.generateKey( response.config.method, response.config.url, response.config.params ); cacheManager.set( response.config.method, response.config.url, response.config.params, response.data ); requestCache.add(cacheKey); } return response; }, error => { if (error.config) { const cacheKey = cacheManager.generateKey( error.config.method, error.config.url, error.config.params ); requestCache.delete(cacheKey); } return Promise.reject(error); } ); }; export const cachedAPI = { get: async (url, params = {}, ttl) => { const method = 'get'; const cached = cacheManager.get(method, url, params); if (cached) { return cached; } return api.get(url, { params }).then(data => { cacheManager.set(method, url, params, data, ttl); return data; }); }, post: (url, data) => api.post(url, data).then(data => { cacheManager.invalidate(url); return data; }), put: (url, data) => api.put(url, data).then(data => { cacheManager.invalidate(url); return data; }), delete: url => api.delete(url).then(data => { cacheManager.invalidate(url); return data; }), invalidate: url => cacheManager.invalidate(url), invalidatePattern: pattern => cacheManager.invalidatePattern(pattern), clearCache: () => cacheManager.clear(), setCacheTTL: (url, ttl) => cacheManager.setTTL(url, ttl), getCacheStats: () => cacheManager.getStats(), }; export const deviceAPI = { list: params => cachedAPI.get('/devices', params), get: deviceId => cachedAPI.get(`/devices/${deviceId}`), create: data => cachedAPI.post('/devices', data), update: (deviceId, data) => cachedAPI.put(`/api/devices/${deviceId}`, data), delete: deviceId => cachedAPI.delete(`/api/devices/${deviceId}`), batchOffline: data => cachedAPI.post('/devices/batch-offline', data), batchDelete: data => cachedAPI.delete('/devices/batch-delete', { data }), export: params => api.get('/devices/export', { params, responseType: 'blob' }), import: formData => api.post('/devices/import', formData, { headers: { 'Content-Type': 'multipart/form-data' }, }), }; export const rackAPI = { list: params => cachedAPI.get('/racks', params), get: rackId => cachedAPI.get(`/racks/${rackId}`), create: data => cachedAPI.post('/racks', data), update: (rackId, data) => cachedAPI.put(`/racks/${rackId}`, data), delete: rackId => cachedAPI.delete(`/racks/${rackId}`), import: formData => api.post('/racks/import', formData, { headers: { 'Content-Type': 'multipart/form-data' }, }), }; export const roomAPI = { list: params => cachedAPI.get('/rooms', params), get: roomId => cachedAPI.get(`/rooms/${roomId}`), create: data => cachedAPI.post('/rooms', data), update: (roomId, data) => cachedAPI.put(`/rooms/${roomId}`, data), delete: roomId => cachedAPI.delete(`/rooms/${roomId}`), }; export const deviceFieldAPI = { list: () => cachedAPI.get('/deviceFields'), get: fieldId => cachedAPI.get(`/deviceFields/${fieldId}`), create: data => cachedAPI.post('/deviceFields', data), update: (fieldId, data) => cachedAPI.put(`/deviceFields/${fieldId}`, data), delete: fieldId => cachedAPI.delete(`/deviceFields/${fieldId}`), updateConfig: data => cachedAPI.post('/deviceFields/config', data), }; export const consumableAPI = { list: params => cachedAPI.get('/consumables', params), get: consumableId => cachedAPI.get(`/consumables/${consumableId}`), create: data => cachedAPI.post('/consumables', data), update: (consumableId, data) => cachedAPI.put(`/consumables/${consumableId}`, data), delete: consumableId => cachedAPI.delete(`/consumables/${consumableId}`), import: data => cachedAPI.post('/consumables/import', data), quickInOut: data => cachedAPI.post('/consumables/quick-inout', data), getStatistics: () => cachedAPI.get('/consumables/statistics/summary'), getLowStock: () => cachedAPI.get('/consumables/low-stock'), getBySn: sn => cachedAPI.get(`/consumables/by-sn/${encodeURIComponent(sn)}`), }; export const consumableCategoryAPI = { list: params => cachedAPI.get('/consumable-categories', params), getList: params => cachedAPI.get('/consumable-categories/list', params), create: data => cachedAPI.post('/consumable-categories', data), update: (id, data) => cachedAPI.put(`/consumable-categories/${id}`, data), delete: id => cachedAPI.delete(`/consumable-categories/${id}`), }; export const consumableLogAPI = { list: params => cachedAPI.get('/consumables/logs', params), create: data => cachedAPI.post('/consumables/logs', data), export: params => api.get('/consumables/logs/export', { params, responseType: 'blob' }), import: data => cachedAPI.post('/consumables/logs/import', data), }; export const consumableRecordAPI = { list: params => cachedAPI.get('/consumable-records', params), statistics: params => api.get('/consumable-records/statistics', { params }), }; export const ticketCategoryAPI = { list: params => cachedAPI.get('/ticket-categories', params), create: data => cachedAPI.post('/ticket-categories', data), update: (code, data) => cachedAPI.put(`/ticket-categories/${code}`, data), delete: code => cachedAPI.delete(`/ticket-categories/${code}`), getTree: () => cachedAPI.get('/ticket-categories/tree'), init: () => cachedAPI.post('/ticket-categories/init'), }; export { cacheManager }; export default { cachedAPI, cacheManager };