You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

461 lines
12 KiB

import type { ApiResponse } from '@/types';
const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000/api/v1';
// 管理员 API 请求
async function adminRequest<T>(path: string, options: RequestInit = {}): Promise<T> {
const token = localStorage.getItem('token');
if (!token) {
throw new Error('请先登录后再操作');
}
const response = await fetch(`${API_BASE_URL}${path}`, {
...options,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
...options.headers,
},
});
if (!response.ok) {
const error = await response.json().catch(() => ({ message: '请求失败' }));
if (response.status === 401) {
// Token 过期或无效,清除本地存储
localStorage.removeItem('token');
localStorage.removeItem('user');
throw new Error('登录已过期,请重新登录');
}
throw new Error(error.message || `HTTP ${response.status}`);
}
const data: ApiResponse<T> = await response.json();
if (data.code !== 200) {
throw new Error(data.message || '请求失败');
}
return data.data;
}
// 用户管理
export interface AdminUser {
id: string;
username: string;
email: string;
role: 'admin' | 'user';
status: 'active' | 'banned';
createdAt: string;
lastLogin: string;
favoritesCount: number;
}
// 数据源配置
export interface DataSourceConfig {
id: string;
name: string;
type: 'akshare' | 'tushare' | 'custom';
url: string;
apiKey?: string;
enabled: boolean;
syncInterval: number;
lastSync?: string;
status: 'connected' | 'disconnected' | 'error';
}
// 数据检查项
export interface DataCheckItem {
id: string;
name: string;
type: 'stock' | 'sector' | 'index' | 'kline';
total: number;
current: number;
lastUpdate: string;
status: 'complete' | 'incomplete' | 'missing';
details?: string;
}
// 同步任务
export interface SyncTask {
id: string;
type: string;
status: 'pending' | 'running' | 'completed' | 'failed';
progress: number;
currentTask: string;
totalRecords: number;
processedRecords: number;
createdAt: string;
completedAt?: string;
error?: string;
}
// 管理员 API
export const adminApi = {
// ========== 数据源管理 ==========
// 获取数据源列表
getDataSources(): Promise<DataSourceConfig[]> {
return adminRequest('/admin/data-sources');
},
// 更新数据源配置
updateDataSource(id: string, config: Partial<DataSourceConfig>): Promise<DataSourceConfig> {
return adminRequest(`/admin/data-sources/${id}`, {
method: 'PUT',
body: JSON.stringify(config),
});
},
// 测试数据源连接
testDataSource(id: string): Promise<{ success: boolean; message: string }> {
return adminRequest(`/admin/data-sources/${id}/test`, {
method: 'POST',
});
},
// 手动触发同步
triggerSync(sourceId: string): Promise<{ taskId: string }> {
return adminRequest(`/admin/data-sources/${sourceId}/sync`, {
method: 'POST',
});
},
// ========== AKShare 特定接口 ==========
// 获取 AKShare 状态
getAKShareStatus(): Promise<{
connected: boolean;
version?: string;
supportedApis: string[];
}> {
return adminRequest('/admin/akshare/status');
},
// 获取 AKShare 配置
getAKShareConfig(): Promise<{
baseUrl: string;
timeout: number;
retryTimes: number;
rateLimit: number;
}> {
return adminRequest('/admin/akshare/config');
},
// 更新 AKShare 配置
updateAKShareConfig(config: {
baseUrl?: string;
timeout?: number;
retryTimes?: number;
rateLimit?: number;
}): Promise<void> {
return adminRequest('/admin/akshare/config', {
method: 'PUT',
body: JSON.stringify(config),
});
},
// ========== 数据检测与缓冲 ==========
// 获取数据完整性检查
getDataCheck(): Promise<DataCheckItem[]> {
return adminRequest('/admin/data-check');
},
// 执行数据完整性检查
runDataCheck(): Promise<{ taskId: string }> {
return adminRequest('/admin/data-check', {
method: 'POST',
});
},
// 获取同步任务进度
getSyncTask(taskId: string): Promise<SyncTask> {
return adminRequest(`/admin/sync-tasks/${taskId}`);
},
// 一键缓冲缺失数据(自动补全一年内的数据)
bufferMissingData(options?: {
startDate?: string;
endDate?: string;
types?: ('stock' | 'sector' | 'kline')[];
}): Promise<{ taskId: string }> {
// 默认缓冲一年内数据
const endDate = new Date();
const startDate = new Date();
startDate.setFullYear(startDate.getFullYear() - 1);
return adminRequest('/admin/buffer', {
method: 'POST',
body: JSON.stringify({
startDate: startDate.toISOString().split('T')[0],
endDate: endDate.toISOString().split('T')[0],
types: ['stock', 'sector', 'kline'],
autoCalculate: true, // 自动计算动量指标
...options,
}),
});
},
// 缓冲特定股票数据
bufferStockData(stockCode: string, days: number = 365): Promise<{ taskId: string }> {
return adminRequest('/admin/buffer/stock', {
method: 'POST',
body: JSON.stringify({ stockCode, days }),
});
},
// 缓冲所有股票基础数据
bufferAllStocks(): Promise<{ taskId: string }> {
return adminRequest('/admin/buffer/stocks', {
method: 'POST',
});
},
// 缓冲版块数据
bufferSectors(): Promise<{ taskId: string }> {
return adminRequest('/admin/buffer/sectors', {
method: 'POST',
});
},
// 缓冲K线数据
bufferKLines(options?: {
stockCodes?: string[];
startDate?: string;
endDate?: string;
}): Promise<{ taskId: string }> {
const endDate = new Date();
const startDate = new Date();
startDate.setFullYear(startDate.getFullYear() - 1);
return adminRequest('/admin/buffer/kline', {
method: 'POST',
body: JSON.stringify({
startDate: startDate.toISOString().split('T')[0],
endDate: endDate.toISOString().split('T')[0],
...options,
}),
});
},
// 计算动量指标
calculateMomentum(options?: {
stockCodes?: string[];
days?: number;
}): Promise<{ taskId: string }> {
return adminRequest('/admin/calculate/momentum', {
method: 'POST',
body: JSON.stringify({
days: 20,
...options,
}),
});
},
// ========== 用户管理 ==========
// 获取用户列表
getUsers(params?: {
page?: number;
pageSize?: number;
search?: string;
role?: string;
status?: string;
}): Promise<{
users: AdminUser[];
total: number;
page: number;
pageSize: number;
}> {
const queryParams = new URLSearchParams();
if (params?.page) queryParams.append('page', params.page.toString());
if (params?.pageSize) queryParams.append('pageSize', params.pageSize.toString());
if (params?.search) queryParams.append('search', params.search);
if (params?.role && params.role !== 'all') queryParams.append('role', params.role);
if (params?.status && params.status !== 'all') queryParams.append('status', params.status);
return adminRequest(`/admin/users?${queryParams.toString()}`);
},
// 更新用户状态(封禁/解封)
updateUserStatus(userId: string, status: 'active' | 'banned'): Promise<void> {
return adminRequest(`/admin/users/${userId}/status`, {
method: 'PUT',
body: JSON.stringify({ status }),
});
},
// 删除用户
deleteUser(userId: string): Promise<void> {
return adminRequest(`/admin/users/${userId}`, {
method: 'DELETE',
});
},
// 批量操作用户
batchUpdateUsers(userIds: string[], action: 'ban' | 'unban' | 'delete'): Promise<void> {
return adminRequest('/admin/users/batch', {
method: 'POST',
body: JSON.stringify({ userIds, action }),
});
},
// ========== 数据导入 ==========
// 上传导入文件
uploadImportFile(file: File, type: string): Promise<{ taskId: string; filename: string }> {
const formData = new FormData();
formData.append('file', file);
formData.append('type', type);
const token = localStorage.getItem('token');
return fetch(`${API_BASE_URL}/admin/import`, {
method: 'POST',
headers: {
'Authorization': token ? `Bearer ${token}` : '',
},
body: formData,
}).then(async (response) => {
if (!response.ok) {
const error = await response.json().catch(() => ({ message: '上传失败' }));
throw new Error(error.message);
}
const data = await response.json();
if (data.code !== 200) {
throw new Error(data.message);
}
return data.data;
});
},
// 获取导入任务列表
getImportTasks(): Promise<{
id: string;
name: string;
fileName: string;
status: string;
progress: number;
totalRecords: number;
importedRecords: number;
createdAt: string;
}[]> {
return adminRequest('/admin/import/tasks');
},
// ========== AI 配置 ==========
// 获取 AI 配置
getAIConfig(): Promise<{
provider: string;
model: string;
apiUrl: string;
temperature: number;
maxTokens: number;
enabled: boolean;
}> {
return adminRequest('/admin/ai-config');
},
// 更新 AI 配置
updateAIConfig(config: {
provider?: string;
model?: string;
apiKey?: string;
apiUrl?: string;
temperature?: number;
maxTokens?: number;
enabled?: boolean;
}): Promise<void> {
return adminRequest('/admin/ai-config', {
method: 'PUT',
body: JSON.stringify(config),
});
},
// 测试 AI 连接
testAIConnection(): Promise<{ success: boolean; message: string }> {
return adminRequest('/admin/ai-config/test', {
method: 'POST',
});
},
// 获取动量计算配置
getMomentumConfig(): Promise<{
calculationPeriod: number;
weightPriceChange: number;
weightVolume: number;
weightTechnical: number;
thresholdStrong: number;
thresholdWeak: number;
}> {
return adminRequest('/admin/momentum-config');
},
// 更新动量计算配置
updateMomentumConfig(config: {
calculationPeriod?: number;
weightPriceChange?: number;
weightVolume?: number;
weightTechnical?: number;
thresholdStrong?: number;
thresholdWeak?: number;
}): Promise<void> {
return adminRequest('/admin/momentum-config', {
method: 'PUT',
body: JSON.stringify(config),
});
},
// ========== 系统统计 ==========
// 获取系统统计
getSystemStats(): Promise<{
totalUsers: number;
totalStocks: number;
totalSectors: number;
dataCompleteness: number;
lastSync: string;
apiStatus: {
akshare: boolean;
database: boolean;
redis: boolean;
};
}> {
return adminRequest('/admin/stats');
},
// 获取数据保留策略
getDataRetention(): Promise<{
stockQuotesDays: number;
klineDays: number;
logsDays: number;
}> {
return adminRequest('/admin/data-retention');
},
// 更新数据保留策略
updateDataRetention(policy: {
stockQuotesDays?: number;
klineDays?: number;
logsDays?: number;
}): Promise<void> {
return adminRequest('/admin/data-retention', {
method: 'PUT',
body: JSON.stringify(policy),
});
},
};
// WebSocket 连接用于实时获取同步进度
export function createSyncTaskWebSocket(taskId: string): WebSocket {
const wsUrl = (import.meta.env.VITE_WS_URL || 'ws://localhost:3000').replace(/^http/, 'ws');
const token = localStorage.getItem('token');
return new WebSocket(`${wsUrl}/ws/sync-tasks/${taskId}?token=${token}`);
}
// Ensure export
export default adminApi;