async function adminFetch(path: string, init?: RequestInit): Promise { const response = await fetch(path, { ...init, credentials: 'include', headers: { Accept: 'application/json', ...(init?.body ? { 'Content-Type': 'application/json' } : {}), ...(init?.headers ?? {}), }, }); const payload = (await response.json().catch(() => ({}))) as { data?: T; message?: string; error?: { code: string; message: string }; }; if (!response.ok) { throw new Error(payload?.error?.message ?? payload?.message ?? `请求失败 (${response.status})`); } return (payload.data !== undefined ? payload.data : payload) as T; } export type AdminUser = { id: string; username: string; slug: string; email: string; displayName: string; role: string; status: string; balanceCents: number; createdAt: string; }; export type AdminSummary = { totalUsers: number; activeUsers: number; totalBalanceCents: number; llm?: { keyCount: number; selectedKeyName: string | null; globalModel: string | null; }; }; export type LedgerEntry = { id: string; userId: string; username?: string; type: string; amountCents: number; note: string; createdAt: string; }; export type UsageRecord = { id: string; userId: string; username?: string; provider: string; model: string; inputTokens: number; outputTokens: number; costCents: number; createdAt: string; }; export type WechatAdminSummary = { config: { mpEnabled: boolean; scheduleEnabled: boolean; reminderWorkerEnabled: boolean; appId: string | null; publicBaseUrl: string | null; bindPath: string | null; tokenEndpointConfigured: boolean; customerServiceEndpointConfigured: boolean; }; counts: { boundUsers: number; routes: { total: number; active: number }; recentMessages: Record; digests: Record; recentDeliveries: Record; }; }; export type WechatBinding = { userId: string; username: string; displayName: string; status: string; appId: string | null; openidMasked: string; nickname: string | null; avatarUrl: string | null; lastLoginAt: number; boundAt: number; routeId: string | null; routeStatus: string | null; agentSessionId: string | null; routeUpdatedAt: number | null; }; export type WechatMessage = { appId: string | null; openidMasked: string; msgId: string; status: string; agentSessionId: string | null; createdAt: number; updatedAt: number; userId: string | null; username: string | null; displayName: string | null; }; export type WechatDigestSubscription = { id: string; userId: string; username: string; displayName: string; digestType: string; hour: number; minute: number; timezone: string; channel: string; status: string; nextRunAt: number; lastRunAt: number | null; attempts: number; lastError: string | null; sourceText: string | null; createdAt: number; updatedAt: number; }; export type WechatDeliveryLog = { id: string; reminderId: string | null; subscriptionId: string | null; userId: string; username: string; displayName: string; channel: string; status: string; providerMessageId: string | null; errorCode: string | null; errorMessage: string | null; createdAt: number; digestType: string | null; }; export type WechatWebNotification = { id: string; userId: string; username: string; displayName: string; channel: string; notificationType: string; title: string; body: string; status: string; readAt: number | null; createdAt: number; updatedAt: number; }; export type CreateWechatNotificationPayload = { userId?: string; userIds?: string[]; audience?: 'all'; allUsers?: boolean; title: string; body: string; notificationType?: string; channel?: 'web' | 'wechat'; channels?: Array<'web' | 'wechat'>; }; export type SystemDisclosureMode = 'off' | 'shadow' | 'enforce'; export type SystemDisclosurePolicyConfig = { enabled: boolean; mode: SystemDisclosureMode; refusalText: string; productNames: string[]; selfReferences: string[]; categories: Record; }; export type SystemDisclosurePolicyState = { config: SystemDisclosurePolicyConfig; policyVersion: number; updatedBy: string | null; updatedAt: number | null; source: string; refreshedAt?: number | null; lastRefreshError?: string | null; }; export type OrchestratorMode = 'off' | 'shadow' | 'canary' | 'active'; export type OrchestratorConfig = { mode: OrchestratorMode; primaryEngine: string; fallbackEngine: string; serviceUrl: string; requestTimeoutMs: number; rolloutPercent: number; userAllowlist: string[]; workflowAllowlist: string[]; fallbackToNative: boolean; requireHealthy: boolean; }; export type OrchestratorRuntime = { killSwitch: boolean; configured: boolean; effective: boolean; reason: string | null; executesLangGraph: boolean; shadowsLangGraph: boolean; }; export type OrchestratorEngineDescriptor = { id: string; label: string; kind: string; configured: boolean; capabilities: string[]; }; export type OrchestratorConfigState = { config: OrchestratorConfig; configVersion: number; updatedBy: string | null; updatedAt: number | null; source: string; runtime: OrchestratorRuntime; engines: OrchestratorEngineDescriptor[]; }; // ─── Summary ────────────────────────────────────────────────────────────────── export async function fetchAdminSummary() { return adminFetch<{ summary: AdminSummary }>('/admin-api/summary'); } // ─── Trust & Policy ─────────────────────────────────────────────────────────── export async function fetchSystemDisclosurePolicy() { return adminFetch('/admin-api/system-disclosure-policy/config'); } export async function updateSystemDisclosurePolicy(config: SystemDisclosurePolicyConfig) { return adminFetch('/admin-api/system-disclosure-policy/config', { method: 'PUT', body: JSON.stringify({ config }), }); } // ─── Workflow Orchestrator ─────────────────────────────────────────────────── export async function fetchOrchestratorConfig() { return adminFetch('/admin-api/orchestrator/config'); } export async function updateOrchestratorConfig(config: OrchestratorConfig) { return adminFetch('/admin-api/orchestrator/config', { method: 'PUT', body: JSON.stringify({ config }), }); } // ─── Users ──────────────────────────────────────────────────────────────────── export async function fetchAdminUsers(params: { page?: number; pageSize?: number; search?: string; role?: string; status?: string; }) { const q = new URLSearchParams(); if (params.page) q.set('page', String(params.page)); if (params.pageSize) q.set('pageSize', String(params.pageSize)); if (params.search) q.set('search', params.search); if (params.role) q.set('role', params.role); if (params.status) q.set('status', params.status); return adminFetch<{ users: AdminUser[]; total: number; page: number; pageSize: number; totalPages: number }>( `/admin-api/users?${q}`, ); } export async function createAdminUser(body: { username: string; password: string; displayName?: string; role?: string; }) { return adminFetch<{ user: AdminUser }>('/admin-api/users', { method: 'POST', body: JSON.stringify(body), }); } export async function patchAdminUser( userId: string, patch: { role?: string; status?: string; displayName?: string; password?: string }, ) { return adminFetch<{ user: AdminUser }>(`/admin-api/users/${userId}`, { method: 'PATCH', body: JSON.stringify(patch), }); } export async function rechargeUser(userId: string, amountCents: number, note = '') { return adminFetch<{ user: AdminUser }>(`/admin-api/users/${userId}/recharge`, { method: 'POST', body: JSON.stringify({ amountCents, note }), }); } // ─── Billing ────────────────────────────────────────────────────────────────── export async function fetchAdminLedger(params: { page?: number; pageSize?: number } = {}) { const q = new URLSearchParams(); if (params.page) q.set('page', String(params.page)); if (params.pageSize) q.set('pageSize', String(params.pageSize)); return adminFetch<{ entries: LedgerEntry[]; total: number; page: number; totalPages: number }>( `/admin-api/ledger?${q}`, ); } export async function fetchAdminUsage(params: { page?: number; pageSize?: number } = {}) { const q = new URLSearchParams(); if (params.page) q.set('page', String(params.page)); if (params.pageSize) q.set('pageSize', String(params.pageSize)); return adminFetch<{ records: UsageRecord[]; total: number; page: number; totalPages: number }>( `/admin-api/usage?${q}`, ); } // ─── WeChat MP ──────────────────────────────────────────────────────────────── export async function fetchWechatSummary() { return adminFetch('/admin-api/wechat/summary'); } export async function fetchWechatBindings(params: { search?: string; limit?: number } = {}) { const q = new URLSearchParams(); if (params.search) q.set('search', params.search); if (params.limit) q.set('limit', String(params.limit)); return adminFetch<{ bindings: WechatBinding[] }>(`/admin-api/wechat/bindings?${q}`); } export async function fetchWechatMessages(params: { status?: string; limit?: number } = {}) { const q = new URLSearchParams(); if (params.status) q.set('status', params.status); if (params.limit) q.set('limit', String(params.limit)); return adminFetch<{ messages: WechatMessage[] }>(`/admin-api/wechat/messages?${q}`); } export async function fetchWechatDigests(params: { status?: string; limit?: number } = {}) { const q = new URLSearchParams(); if (params.status) q.set('status', params.status); if (params.limit) q.set('limit', String(params.limit)); return adminFetch<{ digests: WechatDigestSubscription[] }>(`/admin-api/wechat/digests?${q}`); } export async function fetchWechatDeliveries(params: { status?: string; limit?: number } = {}) { const q = new URLSearchParams(); if (params.status) q.set('status', params.status); if (params.limit) q.set('limit', String(params.limit)); return adminFetch<{ deliveries: WechatDeliveryLog[] }>(`/admin-api/wechat/deliveries?${q}`); } export async function fetchWechatWebNotifications(params: { status?: string; limit?: number } = {}) { const q = new URLSearchParams(); if (params.status) q.set('status', params.status); if (params.limit) q.set('limit', String(params.limit)); return adminFetch<{ notifications: WechatWebNotification[] }>( `/admin-api/wechat/web-notifications?${q}`, ); } export async function createWechatWebNotification(body: CreateWechatNotificationPayload) { return adminFetch<{ ok: boolean; created: number; wechatSent: number; wechatFailures: Array<{ userId: string; message: string }>; targets: number; }>('/admin-api/wechat/web-notifications', { method: 'POST', body: JSON.stringify(body), }); } export async function clearWechatRoute(userId: string) { return adminFetch<{ ok: boolean; deleted: number; openidMasked: string }>( `/admin-api/wechat/users/${userId}/route/clear`, { method: 'POST' }, ); } export async function cancelWechatDigest(id: string) { return adminFetch<{ ok: boolean }>(`/admin-api/wechat/digests/${id}/cancel`, { method: 'POST' }); } export async function resumeWechatDigest(id: string) { return adminFetch<{ ok: boolean; nextRunAt: number }>(`/admin-api/wechat/digests/${id}/resume`, { method: 'POST', }); }