// app/api/client.ts export type ApiResponse = { data?: T; error?: string; status: number; }; export type QueryParams = Record; // 基本数据类型 interface BaseItem { id: string; [key: string]: unknown; } // 为模拟数据预定义类型定义(导出以允许其他文件引用) // 这些类型被用在模拟数据中,虽然没有直接引用 export interface Document extends BaseItem { name: string; type: string; size: number; status: string; uploadDate: string; lastModified: string; } export interface Rule extends BaseItem { code: string; name: string; ruleType: string; groupId: string; groupName: string; priority: string; description: string; isActive: boolean; createdAt: string; updatedAt: string; } export interface RuleGroup extends BaseItem { name: string; description: string; isActive: boolean; } export interface CountItem extends BaseItem { count: number; } // 获取 API 基础 URL,支持服务器端和客户端环境 const API_BASE_URL = typeof process !== 'undefined' && process.env.API_BASE_URL ? process.env.API_BASE_URL : 'http://nas.7bm.co:54302/api/docauditai'; // 如果服务器不可用,会自动使用模拟数据 // 获取 API 访问令牌 const API_TOKEN = typeof process !== 'undefined' && process.env.API_TOKEN ? process.env.API_TOKEN : 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYXBpX3VzZXIifQ.KVLm7rZOF0MuX3MR9LqiYA14gba-MaDK_EQXrJ9u5_Y'; // 是否使用模拟数据(开发环境使用) const USE_MOCK_DATA = true; // 设置为 true 可启用模拟数据 /** * 构建完整的 API URL,支持服务器端和客户端环境 */ function buildUrl(endpoint: string, params?: QueryParams): string { // 创建 URL 字符串 const url = new URL( endpoint.startsWith('http') ? endpoint : API_BASE_URL + endpoint, // 服务器端使用绝对 URL,客户端使用相对 URL typeof window !== 'undefined' ? window.location.origin : 'http://localhost:3000' ); // 添加查询参数 if (params) { Object.entries(params).forEach(([key, value]) => { if (value !== undefined) { url.searchParams.append(key, String(value)); } }); } return url.toString(); } // 超时控制 const fetchWithTimeout = async (url: string, options: RequestInit, timeout = 5000) => { const controller = new AbortController(); const id = setTimeout(() => controller.abort(), timeout); try { const response = await fetch(url, { ...options, signal: controller.signal }); clearTimeout(id); return response; } catch (error) { clearTimeout(id); throw error; } }; // 模拟数据库响应的类型 type MockData = { [key: string]: BaseItem[]; }; // 模拟数据库响应 const mockDataResponses: MockData = { // 文档列表模拟数据 '/documents': [ { id: '1', name: '合同.pdf', type: 'contract', size: 1024000, status: 'approved', uploadDate: new Date().toISOString(), lastModified: new Date().toISOString() }, { id: '2', name: '报告.docx', type: 'report', size: 512000, status: 'pending', uploadDate: new Date().toISOString(), lastModified: new Date().toISOString() } ], // 规则列表模拟数据 '/evaluation_points': [ { id: '1', code: 'R001', name: '合同名称', ruleType: 'essential', groupId: '1', groupName: '合同基本要素类检查', priority: 'high', description: '文档必须包含合同名称', isActive: true, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }, { id: '2', code: 'R002', name: '合同编号', ruleType: 'legal', groupId: '2', groupName: '销售合同专项检查', priority: 'medium', description: '文档必须包含合同编号', isActive: true, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() } ], // 评查点组列表模拟数据 '/evaluation_point_groups': [ { id: '1', name: '合同基本要素类检查', description: '合同基本要素类检查', isActive: true }, { id: '2', name: '销售合同专项检查', description: '销售合同专项检查', isActive: true }, { id: '3', name: '采购合同专项检查', description: '采购合同专项检查', isActive: true }, { id: '4', name: '专卖许可证审核规则', description: '专卖许可证审核规则', isActive: true }, { id: '5', name: '行政处罚规范性检查', description: '行政处罚规范性检查', isActive: true } ], // 计数查询 - 为了满足 BaseItem 类型,添加 id 字段 '/evaluation_points/count': [{ id: 'count', count: 2 }], '/documents/count': [{ id: 'count', count: 2 }] }; /** * 获取模拟响应数据 * @param endpoint API端点 * @param params 查询参数 * @returns 模拟的响应数据 */ function getMockResponse(endpoint: string, params?: QueryParams): ApiResponse { console.log(`[开发模式] 使用模拟数据: ${endpoint}`); // 移除开头的斜杠以便于匹配 const path = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint; // 检查是否有匹配的路径 for (const [mockPath, mockData] of Object.entries(mockDataResponses)) { const normalizedMockPath = mockPath.startsWith('/') ? mockPath.substring(1) : mockPath; if (path === normalizedMockPath || path.startsWith(normalizedMockPath + '/') || path.startsWith(normalizedMockPath + '?')) { // 如果 ID 路径参数 (如 /rules/1),返回单个项目 const pathParts = path.split('/'); const mockPathParts = normalizedMockPath.split('/'); if (pathParts.length > mockPathParts.length && !isNaN(Number(pathParts[mockPathParts.length]))) { const id = pathParts[mockPathParts.length]; const item = mockData.find(i => i.id === id); return item ? { data: item as unknown as T, status: 200 } : { error: '未找到', status: 404 }; } // 处理分页 if (params?.limit && params?.offset) { const limit = Number(params.limit); const offset = Number(params.offset); const paginatedData = mockData.slice(offset, offset + limit); return { data: paginatedData as unknown as T, status: 200 }; } // 返回完整数据 return { data: mockData as unknown as T, status: 200 }; } } // 没有匹配的模拟数据 return { error: '没有匹配的模拟数据', status: 404 }; } /** * 通用 API 请求函数 */ export async function apiRequest( endpoint: string, options: RequestInit = {}, params?: QueryParams ): Promise> { // 如果使用模拟数据,直接返回模拟响应 if (USE_MOCK_DATA) { return getMockResponse(endpoint, params); } try { // 构建 URL const url = buildUrl(endpoint, params); // 设置默认请求头 const headers = new Headers(options.headers || {}); if (!headers.has('Content-Type') && options.method !== 'GET') { headers.set('Content-Type', 'application/json'); } // 数据库连接授权信息 if (!headers.has('Authorization')) { headers.set('Authorization', `Bearer ${API_TOKEN}`); } // 发送请求,5秒超时 const response = await fetchWithTimeout(url, { ...options, headers }, 5000); // 解析响应 let data = null; const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json') && response.status !== 204) { data = await response.json(); } if (!response.ok) { console.error(`API请求失败: ${response.status} - ${url}`); return { error: data?.message || `请求失败: ${response.status}`, status: response.status }; } return { data, status: response.status }; } catch (error) { console.error('API请求失败:', error); // 如果超时或网络错误,使用模拟数据(仅开发环境) if (process.env.NODE_ENV !== 'production') { console.warn('自动使用模拟数据作为回退'); return getMockResponse(endpoint, params); } return { error: error instanceof Error ? error.message : '未知错误', status: 500 }; } }