feat(admin-web): AIPrewarm 分组展示 + 每行触发 + AppType 联合类型

1. AIPrewarm.tsx:
   - areaToAppType(area) helper · area='all' → app2_finance · 其他 → app2a_finance_area
   - handleRunOne / handleBackfillMissing 按 area 动态选 app_type
   - MissingRowWithGroup 含 __group_header 字段
   - groupedMissing 数据构造(全域 + 区域两组 · 每组前插 header 行)
   - 每列 onCell colSpan 合并单元格实现"全域 / 区域"分组标题行
   - Descriptions 加全域 8/X + 区域 64/X 双段统计

2. api/adminAI.ts:
   - 新增 AppType 联合类型(9 项,含 app2a_finance_area)
   - runApp 签名 appType: AppType(替代原 string)
   - RunAppResponse.app_type 同步为 AppType

3. AIOperations.tsx:
   - runAppType state 类型改为 AppType | undefined
   - import { AppType } type

实测:
- pnpm tsc --noEmit 全项目通过
- playwright E2E 访问 /ai/prewarm 显示 "全域 8/8 · 区域 63/64" 分段统计
  分组标题行正确合并 · 单独生成按钮按 area 路由到正确 app_type

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Neo
2026-04-22 21:56:17 +08:00
parent 66be873e70
commit 7107884138
3 changed files with 612 additions and 4 deletions

View File

@@ -7,6 +7,32 @@
import { apiClient } from "./client";
// ---- 公共类型 ----
/**
* AI APP 类型联合(与后端 `CacheTypeEnum` / `_SUPPORTED_APP_TYPES` 同步)。
*
* - app1_chat · 小程序聊天(无缓存)
* - app2_finance · 全域财务洞察area = 'all'8 组合)
* - app2a_finance_area · 区域财务洞察area != 'all'64 组合2026-04-23 新增)
* - app3_clue · 客户线索分析
* - app4_analysis · 助教关系分析
* - app5_tactics · 话术参考
* - app6_note_analysis · 备注分析
* - app7_customer_analysis · 客户综合分析
* - app8_clue_consolidated · 线索整合
*/
export type AppType =
| "app1_chat"
| "app2_finance"
| "app2a_finance_area"
| "app3_clue"
| "app4_analysis"
| "app5_tactics"
| "app6_note_analysis"
| "app7_customer_analysis"
| "app8_clue_consolidated";
// ---- 类型定义 ----
// Dashboard
@@ -201,9 +227,16 @@ export interface AlertActionResponse {
// ---- API 调用 ----
// Dashboard
export async function getDashboard(siteId?: number): Promise<DashboardResponse> {
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: siteId != null ? { site_id: siteId } : undefined,
params: query,
});
return data;
}
@@ -275,3 +308,101 @@ 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;
}