Files
worldquant-alpha-system/frontend/src/api.ts
T

122 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
export class ApiError extends Error {
constructor(
message: string,
public status: number,
) {
super(message);
}
}
export async function api<T>(
path: string,
options: RequestInit = {},
): Promise<T> {
const response = await fetch(`/api/v1${path}`, {
...options,
credentials: "same-origin",
headers: {
"Content-Type": "application/json",
"X-WQ-Request": "1",
...options.headers,
},
});
if (!response.ok) {
const body = await response.json().catch(() => ({}));
if (response.status === 401 && path !== "/auth/login")
window.dispatchEvent(new Event("session-expired"));
throw new ApiError(
typeof body.detail === "string"
? body.detail
: `请求失败(${response.status})`,
response.status,
);
}
return response.json() as Promise<T>;
}
export const post = <T>(path: string, body?: unknown) =>
api<T>(path, {
method: "POST",
body: body === undefined ? undefined : JSON.stringify(body),
});
export const patch = <T>(path: string, body: unknown) =>
api<T>(path, { method: "PATCH", body: JSON.stringify(body) });
export function formatNumber(value: unknown, digits = 2): string {
return typeof value === "number" && Number.isFinite(value)
? value.toFixed(digits)
: "—";
}
export function displayValue(value: unknown): string {
if (value === null || value === undefined || value === "") return "未提供";
if (typeof value === "object") return JSON.stringify(value);
return String(value);
}
export function formatTime(
value: string | null | undefined,
timezone = "Asia/Shanghai",
): string {
if (!value) return "尚未同步";
const date = new Date(value);
if (!Number.isFinite(date.valueOf())) return "时间未知";
return new Intl.DateTimeFormat("zh-CN", {
timeZone: timezone,
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
hour12: false,
}).format(date);
}
export const stateLabels = {
inbox: "待研究",
candidate: "候选",
optimizing: "优化中",
archived: "归档",
};
export const stateOptions = Object.entries(stateLabels).map(
([value, label]) => ({ value, label }),
);
export const jobLabels: Record<string, string> = {
catalog_sync: "同步数据集目录",
field_sync: "同步数据字段",
full_sync: "全量同步 Alpha",
daily_sync: "按天同步 Alpha",
self_correlation: "本地自相关检测",
alpha_refresh: "导入 / 刷新 Alpha",
pnl_refresh: "获取 PnL",
connect: "连接 WorldQuant",
verify: "继续人工验证",
profile: "刷新个人资料",
};
export const correlationLabels = {
high: "相关性偏高",
low: "低于阈值",
partial: "样本不完整",
insufficient_data: "数据不足",
};
export const jobStateLabels: Record<string, string> = {
queued: "排队中",
running: "执行中",
waiting_auth: "等待人工验证",
waiting_connection: "等待连接",
completed: "已完成",
completed_with_errors: "部分失败",
failed: "失败",
cancelled: "已取消",
};
export function queryString(
values: Record<string, string | number | boolean | undefined>,
): string {
return new URLSearchParams(
Object.entries(values)
.filter(([, v]) => v !== undefined && v !== "")
.map(([k, v]) => [k, String(v)]),
).toString();
}