feat:前端修改(我认为ok)

This commit is contained in:
2026-09-14 20:31:06 +08:00
parent 721cc96419
commit 7b6b2a7382
57 changed files with 9558 additions and 0 deletions
+340
View File
@@ -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;
}