涵盖(每条对应已存的审计记录): - AI 模块拆分:apps/backend/app/ai/apps -> prompts/(8 个 APP + app2a 派生) audit: 2026-04-20__ai-module-complete.md - admin-web AI 管理套件:AIDashboard / AIOperations / AIRunLogs / AITriggers / TriggerManager audit: 2026-04-21__admin-web-ai-management-suite.md - App2 财务洞察 prompt v3 -> v5.1 + 小程序 AI 接入(chat / board-finance) audit: 2026-04-22__app2_prompt_v5_1_and_miniprogram_ai_insight.md - App2 prewarm 全过滤器 + AI 触发器 cron reschedule audit: 2026-04-21__app2-finance-prewarm-all-filters.md migration: 20260420_ai_trigger_jobs_and_app2_prewarm.sql / 20260421_app2_prewarm_cron_reschedule.sql - AppType 联合类型对齐 + adminAiAppTypes.test.ts audit: 2026-04-30__admin_web_ai_app_type_alignment.md - DashScope tokens_used 提取修复 audit: 2026-04-30__backend_dashscope_tokens_used_extraction.md - App3 线索完整详情 prompt audit: 2026-05-01__backend_app3_full_detail_prompt.md - Runtime Context 沙箱(5-1~5-2 主线): - 后端 schema/service + admin_runtime_context / xcx_runtime_clock 两个 router - admin-web RuntimeContext.tsx + miniprogram runtime-clock.ts - migration: 20260501__runtime_context_sandbox.sql - tools/db/verify_admin_web_sandbox.py + verify_sandbox_end_to_end.py - database/changes: 7 份 sandbox_* 验证报告 - 飞球 DWS 修复:finance_area_daily 区域汇总 + task_engine 调整 + RLS 视图业务日上界(migration 20260502 + scripts/ops/gen_rls_business_date_migration.py) 合规: - .gitignore 启用 tmp/ 排除 - 不入仓:apps/etl/connectors/feiqiu/.env(API_TOKEN secret,本地修改保留) 待验证清单: - docs/audit/changes/2026-05-04__cumulative_baseline_pending_verification.md 每个主题的功能完整性 / 上线验证几乎都未收口,按优先级 P0~P3 逐一处理
410 lines
10 KiB
TypeScript
410 lines
10 KiB
TypeScript
/**
|
||
* AI 监控后台 API
|
||
*
|
||
* 对接后端 /api/admin/ai/* 端点,提供 Dashboard、调度任务、调用记录、
|
||
* 缓存管理、Token 预算、批量执行、告警管理等功能。
|
||
*/
|
||
|
||
import { apiClient } from "./client";
|
||
|
||
// ---- 公共类型 ----
|
||
|
||
export const RUN_APP_TYPES = [
|
||
"app2_finance",
|
||
"app2a_finance_area",
|
||
"app3_clue",
|
||
"app4_analysis",
|
||
"app5_tactics",
|
||
"app6_note",
|
||
"app7_customer",
|
||
"app8_consolidation",
|
||
] as const;
|
||
|
||
/**
|
||
* 按需执行 App 类型联合(与后端 `/api/admin/ai/run/{app_type}` 同步)。
|
||
*
|
||
* - app2_finance · 全域财务洞察(area = 'all',8 组合)
|
||
* - app2a_finance_area · 区域财务洞察(area != 'all',64 组合,2026-04-23 新增)
|
||
* - app3_clue · 客户线索分析
|
||
* - app4_analysis · 助教关系分析
|
||
* - app5_tactics · 话术参考
|
||
* - app6_note · 备注分析
|
||
* - app7_customer · 客户综合分析
|
||
* - app8_consolidation · 线索整合
|
||
*/
|
||
export type AppType = (typeof RUN_APP_TYPES)[number];
|
||
|
||
// ---- 类型定义 ----
|
||
|
||
// Dashboard
|
||
export interface DailyTrend {
|
||
date: string;
|
||
calls: number;
|
||
success_rate: number;
|
||
}
|
||
|
||
export interface AppDistItem {
|
||
app_type: string;
|
||
count: number;
|
||
percentage: number;
|
||
}
|
||
|
||
export interface BudgetInfo {
|
||
daily_used: number;
|
||
daily_limit: number;
|
||
daily_pct: number;
|
||
monthly_used: number;
|
||
monthly_limit: number;
|
||
monthly_pct: number;
|
||
}
|
||
|
||
export interface AlertItem {
|
||
id: number;
|
||
app_type: string;
|
||
status: string;
|
||
alert_status: string | null;
|
||
error_message: string | null;
|
||
created_at: string;
|
||
}
|
||
|
||
export interface AppHealthItem {
|
||
app_type: string;
|
||
last_status: string | null;
|
||
last_call_at: string | null;
|
||
}
|
||
|
||
export interface DashboardResponse {
|
||
today_calls: number;
|
||
today_success_rate: number;
|
||
today_tokens: number;
|
||
today_avg_latency_ms: number;
|
||
trend_7d: DailyTrend[];
|
||
app_distribution: AppDistItem[];
|
||
budget: BudgetInfo;
|
||
recent_alerts: AlertItem[];
|
||
app_health: AppHealthItem[];
|
||
}
|
||
|
||
// 调度任务
|
||
export interface TriggerJobItem {
|
||
id: number;
|
||
event_type: string;
|
||
member_id: number | null;
|
||
status: string;
|
||
app_chain: string | null;
|
||
is_forced: boolean;
|
||
site_id: number;
|
||
started_at: string | null;
|
||
finished_at: string | null;
|
||
created_at: string;
|
||
}
|
||
|
||
export interface TriggerJobDetailResponse extends TriggerJobItem {
|
||
payload: Record<string, unknown> | null;
|
||
error_message: string | null;
|
||
connector_type: string;
|
||
}
|
||
|
||
export interface TriggerJobListResponse {
|
||
items: TriggerJobItem[];
|
||
total: number;
|
||
page: number;
|
||
page_size: number;
|
||
today_skipped_duplicates: number;
|
||
}
|
||
|
||
export interface RetryResponse {
|
||
trigger_job_id: number;
|
||
status: string;
|
||
}
|
||
|
||
export interface TriggerJobQuery {
|
||
event_type?: string;
|
||
status?: string;
|
||
site_id?: number;
|
||
date_from?: string;
|
||
date_to?: string;
|
||
page?: number;
|
||
page_size?: number;
|
||
}
|
||
|
||
// 调用记录
|
||
export interface RunLogItem {
|
||
id: number;
|
||
app_type: string;
|
||
trigger_type: string;
|
||
member_id: number | null;
|
||
tokens_used: number;
|
||
latency_ms: number | null;
|
||
status: string;
|
||
site_id: number;
|
||
created_at: string;
|
||
}
|
||
|
||
export interface RunLogDetailResponse extends RunLogItem {
|
||
request_prompt: string | null;
|
||
response_text: string | null;
|
||
error_message: string | null;
|
||
session_id: string | null;
|
||
finished_at: string | null;
|
||
}
|
||
|
||
export interface RunLogListResponse {
|
||
items: RunLogItem[];
|
||
total: number;
|
||
page: number;
|
||
page_size: number;
|
||
}
|
||
|
||
export interface RunLogQuery {
|
||
app_type?: string;
|
||
status?: string;
|
||
trigger_type?: string;
|
||
site_id?: number;
|
||
date_from?: string;
|
||
date_to?: string;
|
||
page?: number;
|
||
page_size?: number;
|
||
}
|
||
|
||
// 缓存管理
|
||
export interface CacheInvalidateReq {
|
||
site_id: number;
|
||
app_type?: string;
|
||
member_id?: number;
|
||
}
|
||
|
||
export interface CacheInvalidateResponse {
|
||
affected_count: number;
|
||
}
|
||
|
||
// Token 预算
|
||
export interface BudgetResponse {
|
||
daily_used: number;
|
||
daily_limit: number;
|
||
daily_pct: number;
|
||
monthly_used: number;
|
||
monthly_limit: number;
|
||
monthly_pct: number;
|
||
}
|
||
|
||
// 批量执行
|
||
export interface BatchRunReq {
|
||
app_types: string[];
|
||
member_ids: number[];
|
||
site_id: number;
|
||
}
|
||
|
||
export interface BatchRunEstimate {
|
||
batch_id: string;
|
||
estimated_calls: number;
|
||
estimated_tokens: number;
|
||
}
|
||
|
||
export interface BatchRunConfirmResponse {
|
||
status: string;
|
||
}
|
||
|
||
// 告警
|
||
export interface AlertQuery {
|
||
alert_status?: string;
|
||
site_id?: number;
|
||
page?: number;
|
||
page_size?: number;
|
||
}
|
||
|
||
export interface AlertListResponse {
|
||
items: AlertItem[];
|
||
total: number;
|
||
page: number;
|
||
page_size: number;
|
||
}
|
||
|
||
export interface AlertActionResponse {
|
||
id: number;
|
||
alert_status: string;
|
||
}
|
||
|
||
// ---- API 调用 ----
|
||
|
||
// Dashboard
|
||
export interface DashboardQuery {
|
||
site_id?: number;
|
||
range_days?: number; // 1 / 3 / 7 / 10
|
||
date_from?: string; // YYYY-MM-DD(与 date_to 成对)
|
||
date_to?: string;
|
||
}
|
||
|
||
export async function getDashboard(query?: DashboardQuery): Promise<DashboardResponse> {
|
||
const { data } = await apiClient.get<DashboardResponse>("/admin/ai/dashboard", {
|
||
params: query,
|
||
});
|
||
return data;
|
||
}
|
||
|
||
// 调度任务
|
||
export async function getTriggerJobs(params: TriggerJobQuery): Promise<TriggerJobListResponse> {
|
||
const { data } = await apiClient.get<TriggerJobListResponse>("/admin/ai/trigger-jobs", { params });
|
||
return data;
|
||
}
|
||
|
||
export async function getTriggerJobDetail(id: number): Promise<TriggerJobDetailResponse> {
|
||
const { data } = await apiClient.get<TriggerJobDetailResponse>(`/admin/ai/trigger-jobs/${id}`);
|
||
return data;
|
||
}
|
||
|
||
export async function retryTriggerJob(id: number): Promise<RetryResponse> {
|
||
const { data } = await apiClient.post<RetryResponse>(`/admin/ai/trigger-jobs/${id}/retry`);
|
||
return data;
|
||
}
|
||
|
||
// 调用记录
|
||
export async function getRunLogs(params: RunLogQuery): Promise<RunLogListResponse> {
|
||
const { data } = await apiClient.get<RunLogListResponse>("/admin/ai/run-logs", { params });
|
||
return data;
|
||
}
|
||
|
||
export async function getRunLogDetail(id: number): Promise<RunLogDetailResponse> {
|
||
const { data } = await apiClient.get<RunLogDetailResponse>(`/admin/ai/run-logs/${id}`);
|
||
return data;
|
||
}
|
||
|
||
// 缓存管理
|
||
export async function invalidateCache(body: CacheInvalidateReq): Promise<CacheInvalidateResponse> {
|
||
const { data } = await apiClient.post<CacheInvalidateResponse>("/admin/ai/cache/invalidate", body);
|
||
return data;
|
||
}
|
||
|
||
// Token 预算
|
||
export async function getBudget(): Promise<BudgetResponse> {
|
||
const { data } = await apiClient.get<BudgetResponse>("/admin/ai/budget");
|
||
return data;
|
||
}
|
||
|
||
// 批量执行
|
||
export async function createBatchRun(body: BatchRunReq): Promise<BatchRunEstimate> {
|
||
const { data } = await apiClient.post<BatchRunEstimate>("/admin/ai/batch-run", body);
|
||
return data;
|
||
}
|
||
|
||
export async function confirmBatchRun(batchId: string): Promise<BatchRunConfirmResponse> {
|
||
const { data } = await apiClient.post<BatchRunConfirmResponse>("/admin/ai/batch-run/confirm", {
|
||
batch_id: batchId,
|
||
});
|
||
return data;
|
||
}
|
||
|
||
// 告警
|
||
export async function getAlerts(params: AlertQuery): Promise<AlertListResponse> {
|
||
const { data } = await apiClient.get<AlertListResponse>("/admin/ai/alerts", { params });
|
||
return data;
|
||
}
|
||
|
||
export async function ackAlert(id: number): Promise<AlertActionResponse> {
|
||
const { data } = await apiClient.post<AlertActionResponse>(`/admin/ai/alerts/${id}/ack`);
|
||
return data;
|
||
}
|
||
|
||
export async function ignoreAlert(id: number): Promise<AlertActionResponse> {
|
||
const { data } = await apiClient.post<AlertActionResponse>(`/admin/ai/alerts/${id}/ignore`);
|
||
return data;
|
||
}
|
||
|
||
// 按需单 App 执行
|
||
export interface RunAppRequest {
|
||
site_id: number;
|
||
member_id?: number;
|
||
assistant_id?: number;
|
||
time_dimension?: string;
|
||
area?: string;
|
||
note_content?: string;
|
||
noted_by_name?: string;
|
||
noted_by_created_at?: string;
|
||
}
|
||
|
||
export interface RunAppResponse {
|
||
app_type: AppType;
|
||
success: boolean;
|
||
result: Record<string, unknown> | null;
|
||
error: string | null;
|
||
}
|
||
|
||
export async function runApp(appType: AppType, body: RunAppRequest): Promise<RunAppResponse> {
|
||
const { data } = await apiClient.post<RunAppResponse>(`/admin/ai/run/${appType}`, body);
|
||
return data;
|
||
}
|
||
|
||
// ---- 触发器管理(biz.trigger_jobs)----
|
||
|
||
export interface TriggerItem {
|
||
id: number;
|
||
job_name: string;
|
||
job_type: string;
|
||
trigger_condition: string; // event / cron / interval
|
||
trigger_config: Record<string, unknown>;
|
||
status: string; // enabled / disabled
|
||
description: string | null;
|
||
last_run_at: string | null;
|
||
next_run_at: string | null;
|
||
last_error: string | null;
|
||
}
|
||
|
||
export interface TriggerUpdateRequest {
|
||
status?: string; // enabled / disabled
|
||
cron_expression?: string;
|
||
description?: string;
|
||
}
|
||
|
||
export async function listTriggers(): Promise<TriggerItem[]> {
|
||
const { data } = await apiClient.get<TriggerItem[]>("/admin/ai/triggers");
|
||
return data;
|
||
}
|
||
|
||
export async function updateTrigger(id: number, body: TriggerUpdateRequest): Promise<TriggerItem> {
|
||
const { data } = await apiClient.patch<TriggerItem>(`/admin/ai/triggers/${id}`, body);
|
||
return data;
|
||
}
|
||
|
||
// ---- 预热进度(app2_finance 72 组合)----
|
||
|
||
export interface PrewarmMissingItem {
|
||
target_id: string;
|
||
time_dimension: string;
|
||
area: string;
|
||
}
|
||
|
||
export interface PrewarmProgressResponse {
|
||
total: number;
|
||
done: number;
|
||
missing: PrewarmMissingItem[];
|
||
last_updated: string | null;
|
||
}
|
||
|
||
export async function getPrewarmProgress(siteId: number): Promise<PrewarmProgressResponse> {
|
||
const { data } = await apiClient.get<PrewarmProgressResponse>("/admin/ai/prewarm/progress", {
|
||
params: { site_id: siteId },
|
||
});
|
||
return data;
|
||
}
|
||
|
||
// ---- 手动触发事件链(越过去重)----
|
||
|
||
export interface ManualTriggerRequest {
|
||
event_type: string; // consumption / dws_completed / note_created / task_assigned
|
||
site_id: number;
|
||
member_id?: number;
|
||
assistant_id?: number;
|
||
payload?: Record<string, unknown>;
|
||
is_forced?: boolean;
|
||
}
|
||
|
||
export interface ManualTriggerResponse {
|
||
trigger_job_id: number;
|
||
status: string;
|
||
}
|
||
|
||
export async function triggerEvent(body: ManualTriggerRequest): Promise<ManualTriggerResponse> {
|
||
const { data } = await apiClient.post<ManualTriggerResponse>("/admin/ai/trigger-event", body);
|
||
return data;
|
||
}
|