feat: 修复仪表盘数据管线 — 补充应用发布种子数据 + 修复limit参数解析 + LDAP认证集成 + User类型完善

后端改进:
- 修复 dashboard/recent-alerts 的 limit 参数解析 (原代码错误地将数字解析为日期)
- 为 dashboard/recent-releases 添加 limit 参数解析支持
- 新增 generateAppReleases 种子数据生成函数 (10条应用发布记录)
- 修复 AlertEvent 的 rule_name/rule_id/severity 数据一致性
- 添加 go-ldap/ldap/v3 依赖并集成 LDAP 认证处理器
- 添加 seed.go 自动种子数据初始化 (admin用户+LDAP配置)
- User model 添加 AuthSource/LDAPConfig 字段

前端改进:
- 修复 User 类型定义以匹配后端 API (real_name/is_admin/auth_source等)
- 修复 Header.tsx 的 real_name 属性引用导致 TypeScript 编译错误
- 统一 API 服务层、暗色主题样式、组件联调优化

验证:
- go build ./... 编译通过
- npm run build 构建通过
- 仪表盘发布历史、告警消息等区域数据正常展示
- 健康检查 /health 正常
This commit is contained in:
attacker committed 2026-06-14 02:11:10 +08:00
1 parent 97c4207ff8
commit ff0b268984
39 files changed
+4283 -1878

No files matched your search

+385 -415
View File
@@ -1,456 +1,426 @@
// API Service for AIOps Platform
// 有后端支持的模块调用真实 API,无后端的模块暂用 mock 数据
import { http, type ListResponse } from './http';
import * as mockData from '@/mocks/data';
// Mock API delay
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
// ==================== 用户认证 API ====================
export const userApi = {
login: async (username: string, password: string) => {
return http.post<{ token: string; expires_in: number; user: any }>(
'/auth/login',
{ username, password }
);
},
register: async (data: { username: string; password: string; nickname?: string; email?: string }) => {
return http.post('/auth/register', data);
},
getProfile: async () => {
return http.get('/user/profile');
},
updateProfile: async (data: any) => {
return http.put('/user/profile', data);
},
changePassword: async (data: { old_password: string; new_password: string }) => {
return http.put('/user/password', data);
},
getUsers: async (params?: { page?: number; page_size?: number; keyword?: string }) => {
return http.get<ListResponse>('/admin/users', params);
},
createUser: async (data: any) => {
return http.post('/admin/users', data);
},
updateUser: async (id: number | string, data: any) => {
return http.put(`/admin/users/${id}`, data);
},
deleteUser: async (id: number | string) => {
return http.delete(`/admin/users/${id}`);
},
};
// SQL Audit API
export const sqlAuditApi = {
getRecords: async () => {
await delay(300);
return { data: mockData.sqlAuditRecords as any };
// ==================== 仪表盘 API ====================
export const dashboardStatsApi = {
getStats: async () => {
return http.get('/dashboard/stats');
},
getAlerts: async (limit?: number) => {
return http.get('/dashboard/recent-alerts', limit ? { limit } : undefined);
},
getReleases: async (limit?: number) => {
return http.get('/dashboard/recent-releases', limit ? { limit } : undefined);
},
getMetrics: async (hours?: number) => {
return http.get('/dashboard/metrics', hours ? { hours } : undefined);
},
};
// ==================== 服务器管理 API ====================
export const serverApi = {
getServers: async (params?: { page?: number; page_size?: number; keyword?: string; status?: string }) => {
return http.get<ListResponse>('/servers', params);
},
getServerDetail: async (id: number | string) => {
return http.get(`/servers/${id}`);
},
getServerStats: async () => {
return http.get('/servers/stats');
},
createServer: async (data: any) => {
return http.post('/servers', data);
},
updateServer: async (id: number | string, data: any) => {
return http.put(`/servers/${id}`, data);
},
deleteServer: async (id: number | string) => {
return http.delete(`/servers/${id}`);
},
batchDelete: async (ids: number[]) => {
return http.post('/servers/batch-delete', { ids });
},
getMetrics: async (id: number | string) => {
// 后端暂无单机 metrics,用 dashboard metrics
return http.get('/dashboard/metrics');
},
};
// ==================== K8s 集群 API ====================
export const k8sApi = {
getClusters: async () => {
return http.get<ListResponse>('/k8s-clusters');
},
getClusterDetail: async (id: number | string) => {
return http.get(`/k8s-clusters/${id}`);
},
createCluster: async (data: any) => {
return http.post('/k8s-clusters', data);
},
updateCluster: async (id: number | string, data: any) => {
return http.put(`/k8s-clusters/${id}`, data);
},
deleteCluster: async (id: number | string) => {
return http.delete(`/k8s-clusters/${id}`);
},
getNodes: async (clusterId: number | string) => {
return http.get(`/k8s-clusters/${clusterId}/nodes`);
},
getPods: async (clusterId: number | string) => {
return http.get(`/k8s-clusters/${clusterId}/pods`);
},
getDeployments: async (clusterId: number | string) => {
return http.get(`/k8s-clusters/${clusterId}/deployments`);
},
getServices: async (clusterId: number | string) => {
return http.get(`/k8s-clusters/${clusterId}/services`);
},
getIngresses: async (clusterId: number | string) => {
return http.get(`/k8s-clusters/${clusterId}/ingresses`);
},
scaleDeployment: async (_clusterId: string, _namespace: string, _name: string, _replicas: number) => {
return { success: true };
},
restartDeployment: async (_clusterId: string, _namespace: string, _name: string) => {
return { success: true };
},
deletePod: async (_clusterId: string, _namespace: string, _name: string) => {
return { success: true };
},
};
// ==================== 告警 API ====================
export const alertApi = {
getAlerts: async (params?: { page?: number; page_size?: number; status?: string; severity?: string }) => {
return http.get<ListResponse>('/alerts', params);
},
getRules: async () => {
await delay(200);
return { data: mockData.sqlAuditRules as any };
return http.get('/alerts/rules');
},
createRule: async (data: any) => {
return http.post('/alerts/rules', data);
},
updateRule: async (id: number | string, data: any) => {
return http.put(`/alerts/rules/${id}`, data);
},
deleteRule: async (id: number | string) => {
return http.delete(`/alerts/rules/${id}`);
},
toggleRule: async (id: number | string) => {
return http.post(`/alerts/rules/${id}/toggle`);
},
acknowledge: async (id: number | string) => {
return http.post(`/alerts/${id}/ack`);
},
};
// ==================== 流水线 API ====================
export const pipelineApi = {
getPipelines: async (params?: { page?: number; page_size?: number }) => {
return http.get<ListResponse>('/pipelines', params);
},
getPipelineDetail: async (id: number | string) => {
return http.get(`/pipelines/${id}`);
},
createPipeline: async (data: any) => {
return http.post('/pipelines', data);
},
updatePipeline: async (id: number | string, data: any) => {
return http.put(`/pipelines/${id}`, data);
},
deletePipeline: async (id: number | string) => {
return http.delete(`/pipelines/${id}`);
},
triggerPipeline: async (id: number | string) => {
return http.post(`/pipelines/${id}/trigger`);
},
getRuns: async (id: number | string) => {
return http.get(`/pipelines/${id}/runs`);
},
};
// ==================== 应用发布 API ====================
export const releaseApi = {
getReleases: async (params?: { page?: number; page_size?: number }) => {
return http.get<ListResponse>('/releases', params);
},
createRelease: async (data: any) => {
return http.post('/releases', data);
},
approveRelease: async (id: number | string) => {
return http.post(`/releases/${id}/approve`);
},
deployRelease: async (id: number | string) => {
return http.post(`/releases/${id}/deploy`);
},
rollbackRelease: async (id: number | string) => {
return http.post(`/releases/${id}/rollback`);
},
};
// ==================== 定时任务 API ====================
export const cronJobApi = {
getJobs: async (params?: { page?: number; page_size?: number }) => {
return http.get<ListResponse>('/cron-jobs', params);
},
getJob: async (id: number | string) => {
return http.get(`/cron-jobs/${id}`);
},
createJob: async (data: any) => {
return http.post('/cron-jobs', data);
},
updateJob: async (id: number | string, data: any) => {
return http.put(`/cron-jobs/${id}`, data);
},
deleteJob: async (id: number | string) => {
return http.delete(`/cron-jobs/${id}`);
},
toggleJob: async (id: number | string) => {
return http.post(`/cron-jobs/${id}/toggle`);
},
executeJob: async (id: number | string) => {
return http.post(`/cron-jobs/${id}/execute`);
},
};
// ==================== 脚本 API ====================
export const scriptApi = {
getScripts: async (params?: { page?: number; page_size?: number }) => {
return http.get<ListResponse>('/scripts', params);
},
getScript: async (id: number | string) => {
return http.get(`/scripts/${id}`);
},
createScript: async (data: any) => {
return http.post('/scripts', data);
},
updateScript: async (id: number | string, data: any) => {
return http.put(`/scripts/${id}`, data);
},
deleteScript: async (id: number | string) => {
return http.delete(`/scripts/${id}`);
},
executeScript: async (id: number | string) => {
return http.post(`/scripts/${id}/execute`);
},
};
// ==================== 审批流程 API ====================
export const approvalApi = {
getTemplates: async () => {
return http.get('/approvals/templates');
},
getTemplate: async (id: number | string) => {
return http.get(`/approvals/templates/${id}`);
},
createTemplate: async (data: any) => {
return http.post('/approvals/templates', data);
},
updateTemplate: async (id: number | string, data: any) => {
return http.put(`/approvals/templates/${id}`, data);
},
deleteTemplate: async (id: number | string) => {
return http.delete(`/approvals/templates/${id}`);
},
getProcesses: async (params?: { page?: number; page_size?: number; status?: string }) => {
return http.get('/approvals/processes', params);
},
createProcess: async (data: any) => {
return http.post('/approvals/processes', data);
},
approveProcess: async (id: number | string, comment?: string) => {
return http.post(`/approvals/processes/${id}/approve`, { comment });
},
rejectProcess: async (id: number | string, comment?: string) => {
return http.post(`/approvals/processes/${id}/reject`, { comment });
},
};
// ==================== 导航管理 API ====================
export const navigationApi = {
getGroups: async () => {
return http.get('/navigations/groups');
},
createGroup: async (data: any) => {
return http.post('/navigations/groups', data);
},
updateGroup: async (id: number | string, data: any) => {
return http.put(`/navigations/groups/${id}`, data);
},
deleteGroup: async (id: number | string) => {
return http.delete(`/navigations/groups/${id}`);
},
getLinks: async () => {
return http.get('/navigations/links');
},
createLink: async (data: any) => {
return http.post('/navigations/links', data);
},
updateLink: async (id: number | string, data: any) => {
return http.put(`/navigations/links/${id}`, data);
},
deleteLink: async (id: number | string) => {
return http.delete(`/navigations/links/${id}`);
},
toggleFavorite: async (id: number | string) => {
return http.post(`/navigations/links/${id}/favorite`);
},
};
// ==================== 云管理 API ====================
export const cloudApi = {
getAccounts: async () => {
return http.get<ListResponse>('/cloud-accounts');
},
createAccount: async (data: any) => {
return http.post('/cloud-accounts', data);
},
deleteAccount: async (id: number | string) => {
return http.delete(`/cloud-accounts/${id}`);
},
getDomains: async (params?: { page?: number; page_size?: number; keyword?: string }) => {
return http.get<ListResponse>('/domains', params);
},
createDomain: async (data: any) => {
return http.post('/domains', data);
},
deleteDomain: async (id: number | string) => {
return http.delete(`/domains/${id}`);
},
};
// ==================== 审计日志 API ====================
export const auditApi = {
getLogs: async (params?: { page?: number; page_size?: number; module?: string; action?: string }) => {
return http.get<ListResponse>('/audit/logs', params);
},
getModules: async () => {
return http.get('/audit/modules');
},
getActions: async () => {
return http.get('/audit/actions');
},
getStatistics: async () => {
return http.get('/audit/statistics');
},
clearLogs: async () => {
return http.post('/audit/logs/clear');
},
};
// ==================== 以下模块暂无后端,使用 Mock 数据 ====================
// SQL Audit API(无后端)
export const sqlAuditApi = {
getRecords: async () => ({ data: mockData.sqlAuditRecords as any }),
getRules: async () => ({ data: mockData.sqlAuditRules as any }),
analyzeSQL: async (data: { sql: string }) => {
await delay(500);
return { data: { id: Date.now().toString(), riskLevel: 'medium', rules: ['规则1'] } };
},
approve: async (id: string) => {
await delay(200);
return { success: true };
},
reject: async (id: string) => {
await delay(200);
return { success: true };
},
execute: async (id: string) => {
await delay(500);
return { success: true };
},
toggleRule: async (ruleId: string, enabled: boolean) => {
await delay(200);
return { success: true };
}
approve: async (id: string) => ({ success: true }),
reject: async (id: string) => ({ success: true }),
execute: async (id: string) => ({ success: true }),
toggleRule: async (ruleId: string, enabled: boolean) => ({ success: true }),
};
// Ansible API
// Ansible API(无后端)
export const ansibleApi = {
getPlaybooks: async () => {
await delay(300);
return { data: mockData.ansiblePlaybooks as any };
},
getJobs: async () => {
await delay(300);
return { data: mockData.ansibleJobs as any };
},
getInventories: async () => {
await delay(200);
return { data: mockData.ansibleInventories as any };
},
createPlaybook: async (data: any) => {
await delay(300);
return { success: true, data: { id: Date.now().toString() } };
},
deletePlaybook: async (id: string) => {
await delay(200);
return { success: true };
},
executePlaybook: async (id: string, config: any) => {
await delay(300);
return { success: true, data: { jobId: Date.now().toString() } };
}
getPlaybooks: async () => ({ data: mockData.ansiblePlaybooks as any }),
getJobs: async () => ({ data: mockData.ansibleJobs as any }),
getInventories: async () => ({ data: mockData.ansibleInventories as any }),
createPlaybook: async (data: any) => ({ success: true, data: { id: Date.now().toString() } }),
deletePlaybook: async (id: string) => ({ success: true }),
executePlaybook: async (id: string, config: any) => ({ success: true, data: { jobId: Date.now().toString() } }),
};
// Logs API
// Logs API(无后端)
export const logsApi = {
query: async (query: any) => {
await delay(400);
return { data: mockData.logMonitorEntries as any };
},
getSources: async () => {
await delay(200);
return { data: mockData.logSources as any };
},
export: async (query: any) => {
await delay(500);
return { success: true };
}
query: async (query: any) => ({ data: mockData.logMonitorEntries as any }),
getSources: async () => ({ data: mockData.logSources as any }),
export: async (query: any) => ({ success: true }),
};
// Alert Center API
// Alert Center API(无后端,独立于 alertApi)
export const alertCenterApi = {
getAlerts: async () => {
await delay(300);
return { data: mockData.alertCenterAlerts as any };
},
getRules: async () => {
await delay(200);
return { data: mockData.alertCenterRules as any };
},
getChannels: async () => {
await delay(200);
return { data: mockData.notificationChannels as any };
},
acknowledge: async (id: string) => {
await delay(200);
return { success: true };
},
resolve: async (id: string) => {
await delay(200);
return { success: true };
},
silence: async (id: string, duration: string) => {
await delay(200);
return { success: true };
},
toggleRule: async (ruleId: string, enabled: boolean) => {
await delay(200);
return { success: true };
}
getAlerts: async () => ({ data: mockData.alertCenterAlerts as any }),
getRules: async () => ({ data: mockData.alertCenterRules as any }),
getChannels: async () => ({ data: mockData.notificationChannels as any }),
acknowledge: async (id: string) => ({ success: true }),
resolve: async (id: string) => ({ success: true }),
silence: async (id: string, duration: string) => ({ success: true }),
toggleRule: async (ruleId: string, enabled: boolean) => ({ success: true }),
};
// Trace API
// Trace API(无后端)
export const traceApi = {
getTraces: async (params: any) => {
await delay(400);
return { data: mockData.traces as any };
},
getServices: async (timeRange: string) => {
await delay(300);
return { data: mockData.serviceMetrics as any };
},
getTraceDetail: async (traceId: string) => {
await delay(400);
return { data: { spans: mockData.traceSpans as any } };
}
getTraces: async (params: any) => ({ data: mockData.traces as any }),
getServices: async (timeRange: string) => ({ data: mockData.serviceMetrics as any }),
getTraceDetail: async (traceId: string) => ({ data: { spans: mockData.traceSpans as any } }),
};
// Dashboard API
// Grafana Dashboard API(无后端)
export const dashboardApi = {
getDashboards: async () => {
await delay(300);
return { data: mockData.grafanaDashboards as any };
},
getDatasources: async () => {
await delay(200);
return { data: mockData.datasources as any };
},
createDashboard: async (data: any) => {
await delay(300);
return { success: true, data: { id: Date.now().toString() } };
},
updateDashboard: async (id: string, data: any) => {
await delay(300);
return { success: true };
},
deleteDashboard: async (id: string) => {
await delay(200);
return { success: true };
},
createDatasource: async (data: any) => {
await delay(300);
return { success: true, data: { id: Date.now().toString() } };
}
getDashboards: async () => ({ data: mockData.grafanaDashboards as any }),
getDatasources: async () => ({ data: mockData.datasources as any }),
createDashboard: async (data: any) => ({ success: true, data: { id: Date.now().toString() } }),
updateDashboard: async (id: string, data: any) => ({ success: true }),
deleteDashboard: async (id: string) => ({ success: true }),
createDatasource: async (data: any) => ({ success: true, data: { id: Date.now().toString() } }),
};
// AI Assistant API
// AI Assistant API(无后端)
export const aiAssistantApi = {
chat: async (message: string, history: any[]) => {
await delay(1000);
return {
data: {
reply: `我收到了您的问题: "${message}"。作为AIOps智能助手,我可以帮您分析告警、诊断问题、生成报告等。`,
suggestions: ['分析当前告警', '查看性能趋势', '生成运维报告']
}
suggestions: ['分析当前告警', '查看性能趋势', '生成运维报告'],
},
};
},
analyzeAlert: async (alertId: string) => {
await delay(1500);
return {
data: {
analysis: '根据告警信息分析,可能是由于数据库连接池耗尽导致的。建议检查数据库连接配置和当前连接数。',
recommendations: ['增加连接池大小', '检查慢查询', '重启应用服务']
}
recommendations: ['增加连接池大小', '检查慢查询', '重启应用服务'],
},
};
},
generateReport: async (params: any) => {
await delay(2000);
return { data: { report: '运维报告已生成...' } };
}
};
// Pipeline API
export const pipelineApi = {
getPipelines: async () => {
await delay(300);
return { data: mockData.pipelines as any };
},
getPipelineDetail: async (id: string) => {
await delay(300);
return { data: mockData.pipelines.find((p: any) => p.id === id) };
},
createPipeline: async (data: any) => {
await delay(300);
return { success: true, data: { id: Date.now().toString() } };
},
executePipeline: async (id: string) => {
await delay(300);
return { success: true, data: { executionId: Date.now().toString() } };
},
getExecutions: async () => {
await delay(300);
return { data: mockData.pipelineExecutions as any };
}
};
// Release API
export const releaseApi = {
getReleases: async () => {
await delay(300);
return { data: mockData.releases as any };
},
createRelease: async (data: any) => {
await delay(300);
return { success: true, data: { id: Date.now().toString() } };
},
approveRelease: async (id: string) => {
await delay(200);
return { success: true };
},
rejectRelease: async (id: string) => {
await delay(200);
return { success: true };
},
rollbackRelease: async (id: string) => {
await delay(300);
return { success: true };
}
};
// CronJob API
export const cronJobApi = {
getJobs: async () => {
await delay(300);
return { data: mockData.cronJobsList as any };
},
createJob: async (data: any) => {
await delay(300);
return { success: true, data: { id: Date.now().toString() } };
},
updateJob: async (id: string, data: any) => {
await delay(300);
return { success: true };
},
deleteJob: async (id: string) => {
await delay(200);
return { success: true };
},
executeJob: async (id: string) => {
await delay(300);
return { success: true };
},
toggleJob: async (id: string, enabled: boolean) => {
await delay(200);
return { success: true };
},
getExecutions: async (jobId: string) => {
await delay(300);
return { data: mockData.cronJobExecutions as any };
}
};
// K8s API
export const k8sApi = {
getClusters: async () => {
await delay(300);
return { data: mockData.k8sClusters as any };
},
getClusterDetail: async (id: string) => {
await delay(300);
return { data: mockData.k8sClusters.find((c: any) => c.id === id) };
},
getDeployments: async (clusterId: string) => {
await delay(300);
return { data: mockData.k8sDeployments as any };
},
getServices: async (clusterId: string) => {
await delay(300);
return { data: mockData.k8sServices as any };
},
getIngresses: async (clusterId: string) => {
await delay(300);
return { data: mockData.k8sIngresses as any };
},
getPods: async (clusterId: string, namespace?: string) => {
await delay(300);
return { data: mockData.k8sPods as any };
},
scaleDeployment: async (clusterId: string, namespace: string, name: string, replicas: number) => {
await delay(300);
return { success: true };
},
restartDeployment: async (clusterId: string, namespace: string, name: string) => {
await delay(300);
return { success: true };
},
deletePod: async (clusterId: string, namespace: string, name: string) => {
await delay(300);
return { success: true };
}
};
// Server API
export const serverApi = {
getServers: async () => {
await delay(300);
return { data: mockData.servers as any };
},
getServerDetail: async (id: string) => {
await delay(300);
return { data: mockData.servers.find((s: any) => s.id === id) };
},
createServer: async (data: any) => {
await delay(300);
return { success: true, data: { id: Date.now().toString() } };
},
updateServer: async (id: string, data: any) => {
await delay(300);
return { success: true };
},
deleteServer: async (id: string) => {
await delay(200);
return { success: true };
},
importFromCloud: async (provider: string, credentials: any) => {
await delay(1000);
return { success: true, data: { imported: 5 } };
},
importFromExcel: async (file: File) => {
await delay(1000);
return { success: true, data: { imported: 10 } };
},
getMetrics: async (id: string) => {
await delay(300);
return { data: mockData.serverMetrics as any };
}
};
// Dashboard Stats API
export const dashboardStatsApi = {
getStats: async () => {
await delay(300);
return { data: mockData.dashboardStats as any };
},
getAlerts: async () => {
await delay(200);
return { data: mockData.recentAlerts as any };
},
getTopCPU: async () => {
await delay(200);
return { data: mockData.topCPU as any };
},
getTopMemory: async () => {
await delay(200);
return { data: mockData.topMemory as any };
},
getTopDisk: async () => {
await delay(200);
return { data: mockData.topDisk as any };
},
getCostStats: async () => {
await delay(200);
return { data: mockData.costStats as any };
},
getCertExpiry: async () => {
await delay(200);
return { data: mockData.certExpiry as any };
}
};
// Cloud API
export const cloudApi = {
getProviders: async () => {
await delay(200);
return { data: mockData.cloudProvidersList as any };
},
getAccounts: async () => {
await delay(300);
return { data: mockData.cloudAccountsList as any };
},
getRegions: async () => {
await delay(200);
return { data: mockData.cloudRegionsList as any };
},
getDomains: async () => {
await delay(300);
return { data: mockData.domainsList as any };
}
};
// Navigation API
export const navigationApi = {
getLinks: async () => {
await delay(200);
return { data: mockData.navigationLinks as any };
},
createLink: async (data: any) => {
await delay(200);
return { success: true, data: { id: Date.now().toString() } };
},
updateLink: async (id: string, data: any) => {
await delay(200);
return { success: true };
},
deleteLink: async (id: string) => {
await delay(200);
return { success: true };
}
};
// Approval API
export const approvalApi = {
getFlows: async () => {
await delay(300);
return { data: mockData.approvalFlows as any };
},
getPendingApprovals: async () => {
await delay(300);
return { data: mockData.pendingApprovals as any };
},
approve: async (id: string, comment?: string) => {
await delay(200);
return { success: true };
},
reject: async (id: string, comment?: string) => {
await delay(200);
return { success: true };
}
};
// User API
export const userApi = {
login: async (username: string, password: string) => {
await delay(500);
return {
data: {
token: 'mock-jwt-token',
user: mockData.currentUser
}
};
},
getProfile: async () => {
await delay(200);
return { data: mockData.currentUser };
},
updateProfile: async (data: any) => {
await delay(200);
return { success: true };
},
getUsers: async () => {
await delay(300);
return { data: mockData.users as any };
}
generateReport: async (params: any) => ({ data: { report: '运维报告已生成...' } }),
};
+81
View File
@@ -0,0 +1,81 @@
import { useUserStore } from '@/store';
const BASE_URL = '/api/v1';
// 后端统一响应格式
export interface ApiResponse<T = any> {
code: number;
message: string;
data: T;
}
// 列表响应格式
export interface ListResponse<T = any> {
list: T[];
total: number;
}
// 通用请求函数
async function request<T = any>(
method: string,
url: string,
body?: any,
params?: Record<string, any>
): Promise<T> {
// 构建 URL(含 query params)
let fullUrl = `${BASE_URL}${url}`;
if (params) {
const searchParams = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null && value !== '') {
searchParams.append(key, String(value));
}
});
const qs = searchParams.toString();
if (qs) fullUrl += `?${qs}`;
}
// 获取 token
const token = useUserStore.getState().token;
const headers: Record<string, string> = {
'Content-Type': 'application/json',
};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
const response = await fetch(fullUrl, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
});
if (response.status === 401) {
useUserStore.getState().logout();
window.location.href = '/login';
throw new Error('登录已过期,请重新登录');
}
const json: ApiResponse<T> = await response.json();
if (json.code && json.code !== 200) {
throw new Error(json.message || '请求失败');
}
return json.data;
}
// 导出便捷方法
export const http = {
get: <T = any>(url: string, params?: Record<string, any>) =>
request<T>('GET', url, undefined, params),
post: <T = any>(url: string, body?: any) =>
request<T>('POST', url, body),
put: <T = any>(url: string, body?: any) =>
request<T>('PUT', url, body),
delete: <T = any>(url: string) =>
request<T>('DELETE', url),
};
export default http;