superxu520's picture
feat: 添加支付记录和最近登录字段,优化日期显示
a7623eb
/**
* API 客户端
*/
const API_BASE = process.env.NEXT_PUBLIC_API_URL || '';
export interface KLine {
date: string;
open: number;
high: number;
low: number;
close: number;
volume: number;
amount?: number;
pct_chg?: number;
volume_ratio?: number; // 量比
turnover_rate?: number; // 换手率
}
export interface GameStartResponse {
code: string | null;
real_code: string;
real_name: string;
start_date: string;
klines: KLine[];
total_candles: number;
initial_price: number;
initial_index: number;
}
export interface HealthResponse {
status: string;
database: string;
stocks_count: number;
}
export class ApiError extends Error {
status: number;
data: any;
constructor(message: string, status: number, data: any) {
super(message);
this.name = 'ApiError';
this.status = status;
this.data = data;
}
}
async function fetchAPI<T>(path: string, options?: RequestInit): Promise<T> {
const url = `${API_BASE}${path}`;
const response = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers,
},
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({ detail: 'Unknown error' }));
// 尝试提取错误信息
let message = 'API request failed';
if (typeof errorData.detail === 'string') {
message = errorData.detail;
} else if (errorData.detail && errorData.detail.message) {
message = errorData.detail.message;
} else if (errorData.error) {
message = errorData.error;
}
throw new ApiError(message, response.status, errorData);
}
return response.json();
}
export interface IndexData {
date: string;
close: number;
}
export interface AuthResponse {
token: string;
user_id: number;
username: string;
}
export interface UserInfoResponse {
user_id: number;
username: string;
vip_expire_at: string | null;
is_vip: boolean;
}
export interface VipStatusResponse {
is_vip: boolean;
vip_expire_at: string | null;
}
export interface AdminUserItem {
user_id: number;
username: string;
is_vip: boolean;
vip_expire_at: string | null;
created_at: string;
has_payment: boolean; // 是否有支付记录
last_login: string | null; // 最近登录时间
}
export interface CreatePaymentResponse {
order_id: string;
price: number;
type: number;
}
export interface AdminOrderItem {
order_id: string;
amount: number;
pay_type: number;
status: string;
created_at: string;
paid_at: string | null;
months: number;
}
export const api = {
health: () => fetchAPI<HealthResponse>('/api/health'),
startGame: (mode = 'random', market?: string, token?: string) => {
const params = new URLSearchParams({ mode });
if (market) params.append('market', market);
const headers: Record<string, string> = {};
if (token) headers['Authorization'] = `Bearer ${token}`;
return fetchAPI<GameStartResponse>(`/api/game/start?${params}`, { headers });
},
getKline: (code: string, start?: string, end?: string) => {
const params = new URLSearchParams({ code });
if (start) params.append('start', start);
if (end) params.append('end', end);
return fetchAPI<KLine[]>(`/api/kline?${params}`);
},
getHS300Index: (start?: string, end?: string) => {
const params = new URLSearchParams();
if (start) params.append('start', start);
if (end) params.append('end', end);
return fetchAPI<IndexData[]>(`/api/index/hs300?${params}`);
},
register: (username: string, password: string) =>
fetchAPI<AuthResponse>('/api/v1/auth/register', {
method: 'POST',
body: JSON.stringify({ username, password }),
}),
login: (username: string, password: string) =>
fetchAPI<AuthResponse>('/api/v1/auth/login', {
method: 'POST',
body: JSON.stringify({ username, password }),
}),
me: (token: string) =>
fetchAPI<UserInfoResponse>('/api/v1/auth/me', {
headers: { Authorization: `Bearer ${token}` },
}),
vipStatus: (token: string) =>
fetchAPI<VipStatusResponse>('/api/v1/vip/status', {
headers: { Authorization: `Bearer ${token}` },
}),
logout: (token: string) =>
fetchAPI<{ status: string }>('/api/v1/auth/logout', {
method: 'POST',
headers: { Authorization: `Bearer ${token}` },
}),
getUsageToday: (token: string) =>
fetchAPI<{ used: number; limit: number | null; remaining: number | null; is_vip: boolean }>(
'/api/v1/usage/today',
{ headers: { Authorization: `Bearer ${token}` } }
),
createPayment: (type: number, months: number, token: string) =>
fetchAPI<CreatePaymentResponse>('/api/v1/payment/create', {
method: 'POST',
headers: { Authorization: `Bearer ${token}` },
body: JSON.stringify({ type, months }),
}),
async checkPaymentStatus(orderId: string): Promise<{ code: number; data: { status: number } }> {
return fetchAPI(`/api/v1/payment/check/${orderId}`);
},
// 管理员接口
adminGetUsers: (token: string) =>
fetchAPI<AdminUserItem[]>('/api/v1/admin/users', {
headers: { Authorization: `Bearer ${token}` },
}),
adminUpdatePassword: (userId: number, newPassword: string, token: string) =>
fetchAPI<{ status: string }>(`/api/v1/admin/user/${userId}/password`, {
method: 'POST',
headers: { Authorization: `Bearer ${token}` },
body: JSON.stringify({ new_password: newPassword }),
}),
adminUpdateVip: (userId: number, months: number, token: string) =>
fetchAPI<{ status: string }>(`/api/v1/admin/user/${userId}/vip`, {
method: 'POST',
headers: { Authorization: `Bearer ${token}` },
body: JSON.stringify({ months }),
}),
adminDeleteUser: (userId: number, token: string) =>
fetchAPI<{ status: string }>(`/api/v1/admin/user/${userId}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${token}` },
}),
adminUpdateOrderStatus: (orderId: string, status: string, token: string) =>
fetchAPI<{ status: string }>(`/api/v1/admin/order/${orderId}/status`, {
method: 'PUT',
headers: { Authorization: `Bearer ${token}` },
body: JSON.stringify({ status }),
}),
adminUserOrders: (userId: number, token: string) =>
fetchAPI<AdminOrderItem[]>(`/api/v1/admin/user/${userId}/orders`, {
headers: { Authorization: `Bearer ${token}` },
}),
};