267 lines
9.1 KiB
TypeScript
267 lines
9.1 KiB
TypeScript
import { apiFetch } from "@/lib/api";
|
|
|
|
/** 统一分页响应(后端 utils.pagination.pagination_result)。 */
|
|
export interface Page<T> {
|
|
items: T[];
|
|
total: number;
|
|
page: number;
|
|
page_size: number;
|
|
total_pages: number;
|
|
}
|
|
|
|
export interface TodoItem {
|
|
id: number;
|
|
todo_type: string;
|
|
customer_id: number | null;
|
|
priority: string;
|
|
status: string;
|
|
due_at: string | null;
|
|
}
|
|
|
|
export interface DashboardData {
|
|
todos?: { pending?: number; items?: TodoItem[] };
|
|
overview?: {
|
|
total_customers?: number;
|
|
signed_customers?: number;
|
|
closed_customers?: number;
|
|
churn_rate?: number;
|
|
total_aum?: string;
|
|
reports_sent?: number;
|
|
reports_draft?: number;
|
|
visits_count?: number;
|
|
};
|
|
customer_levels?: Record<string, number>;
|
|
}
|
|
|
|
export interface Customer {
|
|
customer_id: number;
|
|
real_name: string | null;
|
|
phone: string | null;
|
|
risk_level: string | null;
|
|
customer_level: string | null;
|
|
relation_status: string;
|
|
total_assets: number | null;
|
|
}
|
|
|
|
export interface CustomerDetail extends Customer {
|
|
risk_score?: number | null;
|
|
signed_time?: string | null;
|
|
assigned_time?: string | null;
|
|
}
|
|
|
|
export interface Holding {
|
|
product_id: number;
|
|
product_code: string | null;
|
|
product_name: string | null;
|
|
risk_level: string | null;
|
|
shares: string;
|
|
cost_amount: string;
|
|
current_value: string;
|
|
profit_loss: string;
|
|
profit_ratio: string;
|
|
status: string;
|
|
}
|
|
|
|
export interface ReportItem {
|
|
report_id: string;
|
|
intent: string | null;
|
|
title: string | null;
|
|
send_status: string;
|
|
send_time: string | null;
|
|
}
|
|
|
|
export interface Visit {
|
|
id: number;
|
|
customer_id: number;
|
|
advisor_id: number;
|
|
visit_type: string;
|
|
visit_time: string;
|
|
summary: string | null;
|
|
audio_url: string | null;
|
|
create_time: string | null;
|
|
}
|
|
|
|
export interface Diagnosis {
|
|
total_value: string;
|
|
allocation: Record<string, string>;
|
|
concentration: { top_product_ratio: number; top_product: { product_name: string | null } | null };
|
|
holdings: { product_code: string | null; product_name: string | null; product_type: string; current_value: string }[];
|
|
}
|
|
|
|
export interface RiskAlert {
|
|
id: number;
|
|
customer_id: number | null;
|
|
order_id: number | null;
|
|
alert_type: string;
|
|
alert_level: string;
|
|
trigger_detail: string | null;
|
|
transaction_ids: unknown;
|
|
confidence: number | null;
|
|
status: string;
|
|
handler_id: number | null;
|
|
handle_result: string | null;
|
|
handle_time: string | null;
|
|
create_time: string | null;
|
|
}
|
|
|
|
export interface WorkOrder {
|
|
id: number;
|
|
work_order_no: string;
|
|
order_type: string;
|
|
sub_type: string | null;
|
|
customer_id: number | null;
|
|
submitter_id: number | null;
|
|
handler_id: number | null;
|
|
current_node: string | null;
|
|
priority: string;
|
|
status: string;
|
|
biz_content: Record<string, unknown> | null;
|
|
create_time: string | null;
|
|
update_time: string | null;
|
|
}
|
|
|
|
export interface FundProduct {
|
|
id: number;
|
|
product_code: string;
|
|
product_name: string;
|
|
product_type: string;
|
|
risk_level: string;
|
|
nav: number | null;
|
|
nav_date: string | null;
|
|
fund_manager: string | null;
|
|
status: string;
|
|
}
|
|
|
|
export interface Draft {
|
|
draft_id?: string;
|
|
id?: string;
|
|
title?: string;
|
|
content?: string;
|
|
status?: string;
|
|
customer_id?: number | null;
|
|
intent?: string | null;
|
|
create_time?: string | null;
|
|
update_time?: string | null;
|
|
}
|
|
|
|
export interface TalkTemplate {
|
|
scene: string;
|
|
content: string;
|
|
}
|
|
|
|
export interface PersonalReport {
|
|
total_aum: string;
|
|
total_customers: number;
|
|
signed_customers: number;
|
|
reports_sent: number;
|
|
visits_count: number;
|
|
}
|
|
|
|
export interface AuditRow {
|
|
id: number;
|
|
username: string | null;
|
|
module: string | null;
|
|
action: string | null;
|
|
target: string | null;
|
|
detail: string | null;
|
|
status: string | null;
|
|
create_time: string | null;
|
|
}
|
|
|
|
function qs(params: Record<string, string | number | undefined | null>) {
|
|
const search = new URLSearchParams();
|
|
for (const [key, value] of Object.entries(params)) {
|
|
if (value !== undefined && value !== null && value !== "") search.set(key, String(value));
|
|
}
|
|
const text = search.toString();
|
|
return text ? `?${text}` : "";
|
|
}
|
|
|
|
export const adminApi = {
|
|
me: () => apiFetch<{ user: import("@/types/api").UserPayload }>("/auth/me").then((r) => r.user),
|
|
|
|
dashboard: () => apiFetch<DashboardData>("/advisor/dashboard"),
|
|
personalReport: () => apiFetch<PersonalReport>("/advisor/report/personal"),
|
|
|
|
customers: (params: { status?: string; keyword?: string; page?: number; page_size?: number }) =>
|
|
apiFetch<Page<Customer>>(`/advisor/customers${qs(params)}`),
|
|
customer: (id: number, unmask = false) =>
|
|
apiFetch<CustomerDetail>(`/advisor/customers/${id}${qs({ unmask: unmask ? "true" : null })}`),
|
|
holdings: (id: number) => apiFetch<Holding[]>(`/advisor/customers/${id}/holdings`),
|
|
reports: (id: number, page = 1, page_size = 10) =>
|
|
apiFetch<Page<ReportItem>>(`/advisor/customers/${id}/reports${qs({ page, page_size })}`),
|
|
diagnosis: (id: number) => apiFetch<Diagnosis>(`/advisor/diagnosis/${id}`),
|
|
setRelation: (id: number, action: "sign" | "close", reason?: string) =>
|
|
apiFetch<{ customer_id: number; status: string }>(`/advisor/customers/${id}/relation`, {
|
|
method: "POST",
|
|
body: JSON.stringify({ action, reason: reason || null }),
|
|
}),
|
|
|
|
todos: (params: { status?: string; todo_type?: string; page?: number; page_size?: number }) =>
|
|
apiFetch<Page<TodoItem>>(`/advisor/todos${qs(params)}`),
|
|
handleTodo: (id: number, action: "process" | "done") =>
|
|
apiFetch<unknown>(`/advisor/todos/${id}/handle`, {
|
|
method: "POST",
|
|
body: JSON.stringify({ action }),
|
|
}),
|
|
|
|
visits: (params: { customer_id?: number; page?: number; page_size?: number }) =>
|
|
apiFetch<Page<Visit>>(`/advisor/visits${qs(params)}`),
|
|
createVisit: (body: { customer_id: number; visit_type: string; visit_time: string; summary?: string }) =>
|
|
apiFetch<{ visit_id: number }>("/advisor/visits", { method: "POST", body: JSON.stringify(body) }),
|
|
updateVisit: (id: number, body: Partial<{ visit_type: string; visit_time: string; summary: string }>) =>
|
|
apiFetch<Visit>(`/advisor/visits/${id}`, { method: "PUT", body: JSON.stringify(body) }),
|
|
talkTemplates: () => apiFetch<TalkTemplate[]>("/advisor/talk-templates"),
|
|
touchLogs: (customerId: number, page = 1, page_size = 10) =>
|
|
apiFetch<{ messages: unknown[]; visits: Visit[] }>(`/advisor/touch-logs${qs({ customer_id: customerId, page, page_size })}`),
|
|
|
|
drafts: (params: { customer_id?: number; status?: string; page?: number; page_size?: number } = {}) =>
|
|
apiFetch<Page<Draft>>(`/advisor/drafts${qs(params)}`),
|
|
draft: (draftId: string) => apiFetch<Draft>(`/advisor/drafts/${draftId}`),
|
|
saveDraft: (draftId: string, body: { title?: string; content?: string }) =>
|
|
apiFetch<unknown>(`/advisor/drafts/${draftId}/save`, { method: "PUT", body: JSON.stringify(body) }),
|
|
discardDraft: (draftId: string) =>
|
|
apiFetch<unknown>(`/advisor/drafts/${draftId}/discard`, { method: "POST" }),
|
|
sendDraft: (draftId: string) =>
|
|
apiFetch<unknown>(`/advisor/drafts/${draftId}/send`, { method: "POST" }),
|
|
runRebalance: (query: string) =>
|
|
apiFetch<unknown>("/advisor/rebalance/run", { method: "POST", body: JSON.stringify({ query }) }),
|
|
generateTalkScript: (query: string) =>
|
|
apiFetch<{ script?: string; content?: string }>("/advisor/talk-script", {
|
|
method: "POST",
|
|
body: JSON.stringify({ query }),
|
|
}),
|
|
|
|
strategies: () => apiFetch<{ risk_level: string; target_allocation: unknown; drift_threshold: number }[]>("/advisor/strategies"),
|
|
funds: (params: { keyword?: string; product_type?: string; risk_level?: string; page?: number; page_size?: number }) =>
|
|
apiFetch<Page<FundProduct>>(`/advisor/funds${qs(params)}`),
|
|
auditLedger: (params: { page?: number; page_size?: number; action?: string } = {}) =>
|
|
apiFetch<Page<AuditRow>>(`/advisor/audit/ledger${qs(params)}`),
|
|
|
|
alerts: (params: { status?: string; page?: number; page_size?: number } = {}) =>
|
|
apiFetch<Page<RiskAlert>>(`/risk/alert/list${qs(params)}`),
|
|
alertAction: (id: number, action: "release" | "block" | "freeze") =>
|
|
apiFetch<Record<string, unknown>>(`/risk/alert/${id}/${action}`, { method: "POST" }),
|
|
|
|
workOrders: (params: { status?: string; page?: number; page_size?: number } = {}) =>
|
|
apiFetch<Page<WorkOrder>>(`/work-order/list${qs(params)}`),
|
|
workOrder: (id: number) => apiFetch<WorkOrder>(`/work-order/${id}`),
|
|
claimWorkOrder: (id: number) =>
|
|
apiFetch<unknown>("/work-order/claim", { method: "POST", body: JSON.stringify({ work_order_id: id }) }),
|
|
submitWorkOrder: (id: number) =>
|
|
apiFetch<unknown>("/work-order/submit-review", { method: "POST", body: JSON.stringify({ work_order_id: id }) }),
|
|
reviewWorkOrder: (id: number, approve: boolean, comment: string) =>
|
|
apiFetch<unknown>("/work-order/review", {
|
|
method: "POST",
|
|
body: JSON.stringify({ work_order_id: id, approve, comment }),
|
|
}),
|
|
|
|
products: (params: { keyword?: string; page?: number; page_size?: number } = {}) =>
|
|
apiFetch<Page<FundProduct>>(`/products${qs(params)}`),
|
|
};
|
|
|
|
/** 统一错误信息提取,避免每个组件重复判断。 */
|
|
export function errMessage(error: unknown, fallback = "操作失败") {
|
|
return error instanceof Error ? error.message : fallback;
|
|
}
|