feat: 累积功能变更 — 聊天集成、租户管理、小程序更新、ETL 增强、迁移脚本
包含多个会话的累积代码变更: - backend: AI 聊天服务、触发器调度、认证增强、WebSocket、调度器最小间隔 - admin-web: ETL 状态页、任务管理、调度配置、登录优化 - miniprogram: 看板页面、聊天集成、UI 组件、导航更新 - etl: DWS 新任务(finance_area_daily/board_cache)、连接器增强 - tenant-admin: 项目初始化 - db: 19 个迁移脚本(etl_feiqiu 11 + zqyy_app 8) - packages/shared: 枚举和工具函数更新 - tools: 数据库工具、报表生成、健康检查 - docs: PRD/架构/部署/合约文档更新 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
277
apps/admin-web/src/api/adminAI.ts
Normal file
277
apps/admin-web/src/api/adminAI.ts
Normal file
@@ -0,0 +1,277 @@
|
||||
/**
|
||||
* AI 监控后台 API
|
||||
*
|
||||
* 对接后端 /api/admin/ai/* 端点,提供 Dashboard、调度任务、调用记录、
|
||||
* 缓存管理、Token 预算、批量执行、告警管理等功能。
|
||||
*/
|
||||
|
||||
import { apiClient } from "./client";
|
||||
|
||||
// ---- 类型定义 ----
|
||||
|
||||
// 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 async function getDashboard(siteId?: number): Promise<DashboardResponse> {
|
||||
const { data } = await apiClient.get<DashboardResponse>("/admin/ai/dashboard", {
|
||||
params: siteId != null ? { site_id: siteId } : undefined,
|
||||
});
|
||||
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;
|
||||
}
|
||||
@@ -122,20 +122,22 @@ apiClient.interceptors.response.use(
|
||||
|
||||
try {
|
||||
// 用独立 axios 调用避免被自身拦截器干扰
|
||||
const { data } = await axios.post<{
|
||||
access_token: string;
|
||||
refresh_token: string;
|
||||
// ResponseWrapperMiddleware 包装响应为 { code: 0, data: { access_token, refresh_token } }
|
||||
const resp = await axios.post<{
|
||||
code: number;
|
||||
data: { access_token: string; refresh_token: string };
|
||||
}>("/api/auth/refresh", { refresh_token: refreshToken });
|
||||
|
||||
localStorage.setItem(ACCESS_TOKEN_KEY, data.access_token);
|
||||
localStorage.setItem(REFRESH_TOKEN_KEY, data.refresh_token);
|
||||
const tokens = resp.data.data;
|
||||
localStorage.setItem(ACCESS_TOKEN_KEY, tokens.access_token);
|
||||
localStorage.setItem(REFRESH_TOKEN_KEY, tokens.refresh_token);
|
||||
|
||||
processPendingQueue(data.access_token, null);
|
||||
processPendingQueue(tokens.access_token, null);
|
||||
|
||||
// 重放原始请求
|
||||
originalRequest.headers = {
|
||||
...originalRequest.headers,
|
||||
Authorization: `Bearer ${data.access_token}`,
|
||||
Authorization: `Bearer ${tokens.access_token}`,
|
||||
};
|
||||
return apiClient(originalRequest);
|
||||
} catch (refreshError) {
|
||||
|
||||
21
apps/admin-web/src/api/dbHealth.ts
Normal file
21
apps/admin-web/src/api/dbHealth.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* 数据库健康监控 API 调用。
|
||||
*/
|
||||
|
||||
import { apiClient } from './client';
|
||||
|
||||
/** 数据库健康状态 */
|
||||
export interface DbHealthItem {
|
||||
db_name: string;
|
||||
status: 'connected' | 'disconnected';
|
||||
active_connections: number | null;
|
||||
idle_connections: number | null;
|
||||
db_size_mb: number | null;
|
||||
slow_query_count: number | null;
|
||||
}
|
||||
|
||||
/** 获取所有数据库健康状态 */
|
||||
export async function fetchDbHealth(): Promise<DbHealthItem[]> {
|
||||
const { data } = await apiClient.get<DbHealthItem[]>('/admin/db-health');
|
||||
return data;
|
||||
}
|
||||
84
apps/admin-web/src/api/devTrace.ts
Normal file
84
apps/admin-web/src/api/devTrace.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* 开发调试全链路日志 API。
|
||||
*
|
||||
* 对接后端 /api/admin/dev-trace/* 端点,提供日志查询、设置管理、
|
||||
* 覆盖率扫描、手动清理等功能。
|
||||
*/
|
||||
|
||||
import { apiClient } from "./client";
|
||||
import type {
|
||||
TraceFilter,
|
||||
TraceRequest,
|
||||
TraceDetail,
|
||||
TraceSettings,
|
||||
TraceCoverage,
|
||||
} from "../types/devTrace";
|
||||
|
||||
// ---- 响应类型 ----
|
||||
|
||||
export interface TraceRequestListResponse {
|
||||
items: TraceRequest[];
|
||||
total: number;
|
||||
page: number;
|
||||
page_size: number;
|
||||
}
|
||||
|
||||
export interface CleanupResponse {
|
||||
deleted_dates: string[];
|
||||
deleted_files: number;
|
||||
}
|
||||
|
||||
// ---- 日期列表 ----
|
||||
|
||||
export async function fetchDates(): Promise<{ dates: string[] }> {
|
||||
const { data } = await apiClient.get<{ dates: string[] }>("/admin/dev-trace/dates");
|
||||
return data;
|
||||
}
|
||||
|
||||
// ---- 请求列表(分页 + 筛选) ----
|
||||
|
||||
export async function fetchRequests(params: TraceFilter): Promise<TraceRequestListResponse> {
|
||||
const { data } = await apiClient.get<TraceRequestListResponse>("/admin/dev-trace/requests", { params });
|
||||
return data;
|
||||
}
|
||||
|
||||
// ---- 请求详情(含完整 spans) ----
|
||||
|
||||
export async function fetchRequestDetail(requestId: string): Promise<TraceDetail> {
|
||||
const { data } = await apiClient.get<TraceDetail>(`/admin/dev-trace/request/${requestId}`);
|
||||
return data;
|
||||
}
|
||||
|
||||
// ---- 手动清理 ----
|
||||
|
||||
export async function cleanupLogs(startDate: string, endDate: string): Promise<CleanupResponse> {
|
||||
const { data } = await apiClient.post<CleanupResponse>("/admin/dev-trace/cleanup", {
|
||||
start_date: startDate,
|
||||
end_date: endDate,
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
// ---- 设置 ----
|
||||
|
||||
export async function fetchSettings(): Promise<TraceSettings> {
|
||||
const { data } = await apiClient.get<TraceSettings>("/admin/dev-trace/settings");
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function updateSettings(settings: Partial<TraceSettings>): Promise<TraceSettings> {
|
||||
const { data } = await apiClient.put<TraceSettings>("/admin/dev-trace/settings", settings);
|
||||
return data;
|
||||
}
|
||||
|
||||
// ---- 覆盖率 ----
|
||||
|
||||
export async function fetchCoverage(): Promise<TraceCoverage> {
|
||||
const { data } = await apiClient.get<TraceCoverage>("/admin/dev-trace/coverage");
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function triggerCoverageScan(): Promise<TraceCoverage> {
|
||||
const { data } = await apiClient.post<TraceCoverage>("/admin/dev-trace/coverage/scan");
|
||||
return data;
|
||||
}
|
||||
@@ -10,8 +10,8 @@ import { apiClient } from './client';
|
||||
/** ETL 游标信息 */
|
||||
export interface CursorInfo {
|
||||
task_code: string;
|
||||
last_fetch_time: string | null;
|
||||
record_count: number | null;
|
||||
last_start: string | null;
|
||||
last_end: string | null;
|
||||
}
|
||||
|
||||
/** 最近执行记录 */
|
||||
|
||||
@@ -45,3 +45,17 @@ export async function deleteFromQueue(id: string): Promise<void> {
|
||||
export async function cancelExecution(id: string): Promise<void> {
|
||||
await apiClient.post(`/execution/${id}/cancel`);
|
||||
}
|
||||
|
||||
// CHANGE 2026-03-22 | 重新执行历史任务
|
||||
/** 重新执行指定的历史任务 */
|
||||
export async function rerunExecution(id: string): Promise<{ execution_id: string }> {
|
||||
const { data } = await apiClient.post<{ execution_id: string }>(`/execution/${id}/rerun`);
|
||||
return data;
|
||||
}
|
||||
|
||||
// CHANGE 2026-03-27 | 清理输出目录,每类任务只保留最近 10 个运行记录
|
||||
/** 清理 EXPORT_ROOT 下旧运行记录 */
|
||||
export async function cleanupOutput(): Promise<{ task_folders_scanned: number; dirs_deleted: number; errors: string[] }> {
|
||||
const { data } = await apiClient.post<{ task_folders_scanned: number; dirs_deleted: number; errors: string[] }>('/execution/cleanup-output');
|
||||
return data;
|
||||
}
|
||||
|
||||
134
apps/admin-web/src/api/registry.ts
Normal file
134
apps/admin-web/src/api/registry.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* 注册体系 API 调用(租户/店铺/简写ID/同步)。
|
||||
*
|
||||
* 复用 apiClient(已含 JWT 拦截器 + 响应解包)。
|
||||
* 端点前缀:/admin
|
||||
*/
|
||||
|
||||
import { apiClient } from "./client";
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* 类型定义 */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/** 租户列表项(后端 CamelModel 序列化为 camelCase) */
|
||||
export interface TenantItem {
|
||||
id: number;
|
||||
tenantId: number;
|
||||
tenantName: string;
|
||||
connectorName: string;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
/** 店铺列表项 */
|
||||
export interface SiteItem {
|
||||
id: number;
|
||||
siteId: number;
|
||||
siteName: string;
|
||||
siteCode: string | null;
|
||||
siteLabel: string | null;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
/** 设置/修改简写ID 请求 */
|
||||
export interface UpdateSiteCodeRequest {
|
||||
newCode: string;
|
||||
}
|
||||
|
||||
/** 简写ID 修改结果 */
|
||||
export interface SiteCodeResult {
|
||||
siteId: number;
|
||||
oldCode: string | null;
|
||||
newCode: string;
|
||||
historyCleaned: boolean;
|
||||
}
|
||||
|
||||
/** 简写ID 变更历史条目 */
|
||||
export interface SiteCodeHistoryItem {
|
||||
id: number;
|
||||
siteCode: string;
|
||||
isCurrent: boolean;
|
||||
createdAt: string;
|
||||
retiredAt: string | null;
|
||||
}
|
||||
|
||||
/** 店铺同步结果 */
|
||||
export interface SiteSyncResult {
|
||||
inserted: number;
|
||||
updated: number;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* API 调用 */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/** 获取所有活跃租户列表 */
|
||||
export async function fetchTenants(): Promise<TenantItem[]> {
|
||||
const { data } = await apiClient.get<TenantItem[]>("/admin/tenants");
|
||||
return data;
|
||||
}
|
||||
|
||||
/** 获取指定租户下所有活跃店铺 */
|
||||
export async function fetchTenantSites(
|
||||
tenantId: number,
|
||||
): Promise<SiteItem[]> {
|
||||
const { data } = await apiClient.get<SiteItem[]>(
|
||||
`/admin/tenants/${tenantId}/sites`,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
/** 设置/修改店铺简写ID */
|
||||
export async function updateSiteCode(
|
||||
siteId: number,
|
||||
newCode: string,
|
||||
): Promise<SiteCodeResult> {
|
||||
const { data } = await apiClient.put<SiteCodeResult>(
|
||||
`/admin/sites/${siteId}/site-code`,
|
||||
{ newCode } satisfies UpdateSiteCodeRequest,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
/** 查看简写ID 变更历史 */
|
||||
export async function fetchSiteCodeHistory(
|
||||
siteId: number,
|
||||
): Promise<SiteCodeHistoryItem[]> {
|
||||
const { data } = await apiClient.get<SiteCodeHistoryItem[]>(
|
||||
`/admin/sites/${siteId}/site-code-history`,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
/** 手动触发店铺同步 */
|
||||
export async function syncSites(): Promise<SiteSyncResult> {
|
||||
const { data } = await apiClient.post<SiteSyncResult>(
|
||||
"/admin/sites/sync",
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* 测试功能:手动创建/删除店铺 */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/** 创建店铺请求 */
|
||||
export interface CreateSitePayload {
|
||||
tenantId: number;
|
||||
siteId: number;
|
||||
siteName: string;
|
||||
siteCode?: string;
|
||||
}
|
||||
|
||||
/** 手动创建店铺(测试功能) */
|
||||
export async function createSite(
|
||||
payload: CreateSitePayload,
|
||||
): Promise<SiteItem> {
|
||||
const { data } = await apiClient.post<SiteItem>("/admin/sites", payload);
|
||||
return data;
|
||||
}
|
||||
|
||||
/** 删除店铺(测试功能,硬删除) */
|
||||
export async function deleteSite(id: number): Promise<void> {
|
||||
await apiClient.delete(`/admin/sites/${id}`);
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { apiClient } from './client';
|
||||
import type { ScheduledTask, ScheduleConfig, TaskConfig, ExecutionLog } from '../types';
|
||||
import type { ScheduledTask, ScheduleConfig, TaskConfig, ExecutionLog, MinRunIntervalItem } from '../types';
|
||||
|
||||
/** 获取调度任务列表 */
|
||||
export async function fetchSchedules(): Promise<ScheduledTask[]> {
|
||||
@@ -18,6 +18,9 @@ export async function createSchedule(payload: {
|
||||
task_config: TaskConfig;
|
||||
schedule_config: ScheduleConfig;
|
||||
run_immediately?: boolean;
|
||||
min_run_interval_value?: number;
|
||||
min_run_interval_unit?: string;
|
||||
min_run_intervals?: Record<string, MinRunIntervalItem>;
|
||||
}): Promise<ScheduledTask> {
|
||||
const { data } = await apiClient.post<ScheduledTask>('/schedules', payload);
|
||||
return data;
|
||||
@@ -31,6 +34,9 @@ export async function updateSchedule(
|
||||
task_codes: string[];
|
||||
task_config: TaskConfig;
|
||||
schedule_config: ScheduleConfig;
|
||||
min_run_interval_value: number;
|
||||
min_run_interval_unit: string;
|
||||
min_run_intervals: Record<string, MinRunIntervalItem>;
|
||||
}>,
|
||||
): Promise<ScheduledTask> {
|
||||
const { data } = await apiClient.put<ScheduledTask>(`/schedules/${id}`, payload);
|
||||
@@ -49,8 +55,10 @@ export async function toggleSchedule(id: string): Promise<ScheduledTask> {
|
||||
}
|
||||
|
||||
/** 手动执行调度任务一次(不更新调度间隔) */
|
||||
export async function runScheduleNow(id: string): Promise<{ message: string; task_id: string }> {
|
||||
const { data } = await apiClient.post<{ message: string; task_id: string }>(`/schedules/${id}/run`);
|
||||
export async function runScheduleNow(id: string, force = false): Promise<{ message: string; task_id: string }> {
|
||||
const { data } = await apiClient.post<{ message: string; task_id: string }>(`/schedules/${id}/run`, null, {
|
||||
params: force ? { force: true } : undefined,
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
187
apps/admin-web/src/api/taskEngine.ts
Normal file
187
apps/admin-web/src/api/taskEngine.ts
Normal file
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
* P18 任务引擎运营看板 API。
|
||||
*
|
||||
* 端点前缀:/api/admin/task-engine
|
||||
*/
|
||||
|
||||
import { apiClient } from "./client";
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* 类型定义 */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
export interface TransferLogItem {
|
||||
id: number;
|
||||
site_id: number;
|
||||
site_name: string;
|
||||
member_id: number;
|
||||
member_name: string;
|
||||
from_assistant_id: number;
|
||||
from_assistant_name: string;
|
||||
to_assistant_id: number;
|
||||
to_assistant_name: string;
|
||||
transfer_reason: string | null;
|
||||
transfer_score: number | null;
|
||||
guard_checks: Record<string, unknown> | null;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface TransferLogPage {
|
||||
items: TransferLogItem[];
|
||||
total: number;
|
||||
}
|
||||
|
||||
export interface PendingReviewItem {
|
||||
id: number;
|
||||
site_id: number;
|
||||
site_name: string;
|
||||
member_id: number;
|
||||
member_name: string;
|
||||
assistant_id: number;
|
||||
assistant_name: string;
|
||||
task_type: string;
|
||||
task_type_label: string;
|
||||
transfer_count: number;
|
||||
priority_score: number | null;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface PendingReviewPage {
|
||||
items: PendingReviewItem[];
|
||||
total: number;
|
||||
}
|
||||
|
||||
export interface ConfigParam {
|
||||
id: number;
|
||||
site_id: number | null;
|
||||
site_name: string | null;
|
||||
param_key: string;
|
||||
param_value: number;
|
||||
description: string | null;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface ConfigParamList {
|
||||
params: ConfigParam[];
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* 转移日志 */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
export interface TransferLogQuery {
|
||||
site_id?: number;
|
||||
from_date?: string;
|
||||
to_date?: string;
|
||||
assistant_id?: number;
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
}
|
||||
|
||||
export async function fetchTransferLogs(
|
||||
query: TransferLogQuery = {},
|
||||
): Promise<TransferLogPage> {
|
||||
const resp = await apiClient.get<TransferLogPage>(
|
||||
"/admin/task-engine/transfer-log",
|
||||
{ params: query },
|
||||
);
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
export async function fetchMemberTransferHistory(
|
||||
memberId: number,
|
||||
): Promise<TransferLogItem[]> {
|
||||
const resp = await apiClient.get<TransferLogItem[]>(
|
||||
`/admin/task-engine/transfer-log/${memberId}/history`,
|
||||
);
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* 待审核任务 */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
export interface PendingReviewQuery {
|
||||
site_id?: number;
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
}
|
||||
|
||||
export async function fetchPendingReviews(
|
||||
query: PendingReviewQuery = {},
|
||||
): Promise<PendingReviewPage> {
|
||||
const resp = await apiClient.get<PendingReviewPage>(
|
||||
"/admin/task-engine/pending-review",
|
||||
{ params: query },
|
||||
);
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
export async function reassignTask(
|
||||
taskId: number,
|
||||
toAssistantId: number,
|
||||
): Promise<{ success: boolean; new_task_id: number | null }> {
|
||||
const resp = await apiClient.post(
|
||||
`/admin/task-engine/pending-review/${taskId}/reassign`,
|
||||
{ to_assistant_id: toAssistantId },
|
||||
);
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
export async function closeTask(
|
||||
taskId: number,
|
||||
reason: string,
|
||||
): Promise<{ success: boolean }> {
|
||||
const resp = await apiClient.post(
|
||||
`/admin/task-engine/pending-review/${taskId}/close`,
|
||||
{ reason },
|
||||
);
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* 参数管理 */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
export async function fetchConfigParams(
|
||||
siteId?: number,
|
||||
): Promise<ConfigParamList> {
|
||||
const resp = await apiClient.get<ConfigParamList>(
|
||||
"/admin/task-engine/config",
|
||||
{ params: siteId != null ? { site_id: siteId } : {} },
|
||||
);
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
export async function updateConfigParam(
|
||||
paramId: number,
|
||||
paramValue: number,
|
||||
): Promise<{ success: boolean }> {
|
||||
const resp = await apiClient.put(
|
||||
`/admin/task-engine/config/${paramId}`,
|
||||
{ param_value: paramValue },
|
||||
);
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
export async function createConfigParam(
|
||||
siteId: number,
|
||||
paramKey: string,
|
||||
paramValue: number,
|
||||
): Promise<{ success: boolean; id: number }> {
|
||||
const resp = await apiClient.post("/admin/task-engine/config", {
|
||||
site_id: siteId,
|
||||
param_key: paramKey,
|
||||
param_value: paramValue,
|
||||
});
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
export async function deleteConfigParam(
|
||||
paramId: number,
|
||||
): Promise<{ success: boolean }> {
|
||||
const resp = await apiClient.delete(
|
||||
`/admin/task-engine/config/${paramId}`,
|
||||
);
|
||||
return resp.data;
|
||||
}
|
||||
110
apps/admin-web/src/api/tenantAdmins.ts
Normal file
110
apps/admin-web/src/api/tenantAdmins.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* 租户管理员 CRUD API 调用。
|
||||
*
|
||||
* 复用 apiClient(已含 JWT 拦截器 + 响应解包)。
|
||||
* 端点前缀:/admin/tenant-admins
|
||||
*/
|
||||
|
||||
import { apiClient } from "./client";
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* 类型定义 */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/** 列表项(后端 CamelModel 序列化为 camelCase) */
|
||||
export interface TenantAdminItem {
|
||||
id: number;
|
||||
username: string;
|
||||
displayName: string | null;
|
||||
tenantId: number;
|
||||
tenantName: string | null;
|
||||
adminType: string;
|
||||
managedSiteIds: number[];
|
||||
isActive: boolean;
|
||||
createdAt: string;
|
||||
lastLoginAt: string | null;
|
||||
}
|
||||
|
||||
/** 分页响应 */
|
||||
export interface TenantAdminListResponse {
|
||||
items: TenantAdminItem[];
|
||||
total: number;
|
||||
page: number;
|
||||
page_size: number;
|
||||
}
|
||||
|
||||
/** 创建请求 */
|
||||
export interface TenantAdminCreatePayload {
|
||||
username: string;
|
||||
password: string;
|
||||
displayName: string;
|
||||
tenantId: number;
|
||||
managedSiteIds: number[];
|
||||
}
|
||||
|
||||
/** 编辑请求(所有字段可选) */
|
||||
export interface TenantAdminEditPayload {
|
||||
username?: string;
|
||||
displayName?: string;
|
||||
managedSiteIds?: number[];
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
/** 重置密码请求 */
|
||||
export interface ResetPasswordPayload {
|
||||
newPassword: string;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* API 调用 */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/** 列表查询(分页 + 关键词搜索 + 可选包含已禁用) */
|
||||
export async function fetchTenantAdmins(params: {
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
keyword?: string;
|
||||
include_inactive?: boolean;
|
||||
}): Promise<TenantAdminListResponse> {
|
||||
const { data } = await apiClient.get<TenantAdminListResponse>(
|
||||
"/admin/tenant-admins",
|
||||
{ params },
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
/** 创建租户管理员 */
|
||||
export async function createTenantAdmin(
|
||||
payload: TenantAdminCreatePayload,
|
||||
): Promise<TenantAdminItem> {
|
||||
const { data } = await apiClient.post<TenantAdminItem>(
|
||||
"/admin/tenant-admins",
|
||||
payload,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
/** 编辑租户管理员 */
|
||||
export async function editTenantAdmin(
|
||||
id: number,
|
||||
payload: TenantAdminEditPayload,
|
||||
): Promise<TenantAdminItem> {
|
||||
const { data } = await apiClient.patch<TenantAdminItem>(
|
||||
`/admin/tenant-admins/${id}`,
|
||||
payload,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
/** 重置密码 */
|
||||
export async function resetTenantAdminPassword(
|
||||
id: number,
|
||||
payload: ResetPasswordPayload,
|
||||
): Promise<void> {
|
||||
await apiClient.post(`/admin/tenant-admins/${id}/reset-password`, payload);
|
||||
}
|
||||
|
||||
/** 删除租户管理员(软删除) */
|
||||
export async function deleteTenantAdmin(id: number): Promise<void> {
|
||||
await apiClient.delete(`/admin/tenant-admins/${id}`);
|
||||
}
|
||||
62
apps/admin-web/src/api/triggerJobs.ts
Normal file
62
apps/admin-web/src/api/triggerJobs.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* 定时任务管理 API 调用。
|
||||
*/
|
||||
|
||||
import { apiClient } from './client';
|
||||
|
||||
/** 定时任务信息 */
|
||||
export interface TriggerJob {
|
||||
id: number;
|
||||
job_type: string;
|
||||
job_name: string;
|
||||
trigger_condition: string;
|
||||
trigger_config: Record<string, unknown> | null;
|
||||
last_run_at: string | null;
|
||||
next_run_at: string | null;
|
||||
status: string;
|
||||
description: string | null;
|
||||
last_error: string | null;
|
||||
created_at: string | null;
|
||||
}
|
||||
|
||||
/** 手动执行结果 */
|
||||
export interface RunJobResult {
|
||||
success: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
/** 获取所有定时任务 */
|
||||
export async function fetchTriggerJobs(): Promise<TriggerJob[]> {
|
||||
const { data } = await apiClient.get<TriggerJob[]>('/trigger-jobs');
|
||||
return data;
|
||||
}
|
||||
|
||||
/** 手动执行指定任务 */
|
||||
export async function runTriggerJob(jobId: number): Promise<RunJobResult> {
|
||||
const { data } = await apiClient.post<RunJobResult>(`/trigger-jobs/${jobId}/run`);
|
||||
return data;
|
||||
}
|
||||
|
||||
/** 触发器配置更新请求 */
|
||||
export interface UpdateTriggerConfigReq {
|
||||
cron_expression?: string;
|
||||
interval_seconds?: number;
|
||||
}
|
||||
|
||||
/** 更新触发器配置(cron 表达式或间隔秒数) */
|
||||
export async function updateTriggerConfig(
|
||||
jobId: number,
|
||||
body: UpdateTriggerConfigReq,
|
||||
): Promise<TriggerJob> {
|
||||
const { data } = await apiClient.patch<TriggerJob>(
|
||||
`/trigger-jobs/${jobId}/config`,
|
||||
body,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
/** 【测试】清空所有 coach_tasks */
|
||||
export async function clearAllTasks(): Promise<RunJobResult> {
|
||||
const { data } = await apiClient.delete<RunJobResult>('/admin/task-engine/clear-all-tasks');
|
||||
return data;
|
||||
}
|
||||
23
apps/admin-web/src/api/triggers.ts
Normal file
23
apps/admin-web/src/api/triggers.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 触发器统一视图 API 调用。
|
||||
*/
|
||||
|
||||
import { apiClient } from './client';
|
||||
|
||||
/** 统一触发器条目 */
|
||||
export interface UnifiedTriggerItem {
|
||||
id: number;
|
||||
name: string;
|
||||
source: 'biz' | 'ai' | 'etl';
|
||||
trigger_condition: string;
|
||||
status: string;
|
||||
last_run_at: string | null;
|
||||
next_run_at: string | null;
|
||||
last_error: string | null;
|
||||
}
|
||||
|
||||
/** 获取所有触发器统一视图 */
|
||||
export async function fetchUnifiedTriggers(): Promise<UnifiedTriggerItem[]> {
|
||||
const { data } = await apiClient.get<UnifiedTriggerItem[]>('/admin/triggers/unified');
|
||||
return data;
|
||||
}
|
||||
Reference in New Issue
Block a user