feat:前端修改(我认为ok)
This commit is contained in:
@@ -0,0 +1,340 @@
|
||||
import { apiFetch, apiFetchBlob, downloadBlob } 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;
|
||||
}
|
||||
|
||||
/** GET /products/{code}/history —— 走势图数据(series=nav|return)。 */
|
||||
export interface FundNavHistory {
|
||||
product_code: string;
|
||||
series: string;
|
||||
total_growth_rate: number;
|
||||
max_return_rate: number;
|
||||
max_drawdown_rate: number;
|
||||
items: { nav_date: string; nav?: number | null; return_rate?: number | null }[];
|
||||
}
|
||||
|
||||
/** POST /advisor/data-query —— 工作台代理的 NL2SQL 查询结果。 */
|
||||
export interface AdvisorDataQueryResult {
|
||||
query_id?: string;
|
||||
answer?: string;
|
||||
summary?: string;
|
||||
columns?: string[];
|
||||
rows?: Record<string, unknown>[];
|
||||
row_count?: number;
|
||||
truncated?: boolean;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/** GET /profile/composed —— 画像 + 记忆 + 持仓三源整合结果。 */
|
||||
export interface ComposedProfile {
|
||||
customer_id?: number;
|
||||
profile?: Record<string, unknown> | null;
|
||||
memories?: unknown[];
|
||||
holdings?: unknown[];
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/** GET /health/ready —— 四库就绪探测。 */
|
||||
export interface HealthReady {
|
||||
ready?: boolean;
|
||||
checks?: Record<string, unknown>;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
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)}`),
|
||||
|
||||
/** GET /products/{product_code}/history —— 历史净值/收益序列,series 仅支持 nav|return。 */
|
||||
productHistory: (
|
||||
productCode: string,
|
||||
params: { start_date?: string; end_date?: string; series?: "nav" | "return" } = {}
|
||||
) =>
|
||||
apiFetch<FundNavHistory>(`/products/${encodeURIComponent(productCode)}/history${qs(params)}`),
|
||||
|
||||
/** POST /advisor/data-query —— 工作台侧代理的 NL2SQL 客户数据查询。 */
|
||||
advisorDataQuery: (body: {
|
||||
query: string;
|
||||
session_id?: string;
|
||||
max_rows?: number;
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
}) =>
|
||||
apiFetch<AdvisorDataQueryResult>("/advisor/data-query", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(body),
|
||||
}),
|
||||
|
||||
/** GET /advisor/visits/{id} —— 回访详情(列表接口之外的单条读取)。 */
|
||||
visit: (visitId: number) => apiFetch<Visit>(`/advisor/visits/${visitId}`),
|
||||
|
||||
/** GET /advisor/audit/export —— 导出本人审计台账 CSV。 */
|
||||
exportAuditLedger: async (params: { action?: string } = {}) => {
|
||||
const blob = await apiFetchBlob(`/advisor/audit/export${qs(params)}`);
|
||||
downloadBlob(blob, `audit-ledger-${new Date().toISOString().slice(0, 10)}.csv`);
|
||||
},
|
||||
|
||||
/** GET /profile/composed —— 画像 + 记忆 + 持仓三源整合,仅员工可查。 */
|
||||
composedProfile: (customerId: number) =>
|
||||
apiFetch<ComposedProfile>(`/profile/composed${qs({ customer_id: customerId })}`),
|
||||
|
||||
/** GET /health/ready —— 四库就绪探测。 */
|
||||
healthReady: () => apiFetch<HealthReady>("/health/ready"),
|
||||
};
|
||||
|
||||
/** 统一错误信息提取,避免每个组件重复判断。 */
|
||||
export function errMessage(error: unknown, fallback = "操作失败") {
|
||||
return error instanceof Error ? error.message : fallback;
|
||||
}
|
||||
Reference in New Issue
Block a user