import type { AssistantCapture, MemoryCandidate, MemoryConfirmRequest, MemoryConfirmResult, PersonalAskResponse, PersonalExportOutline, PersonalExportOutlineSection, PersonalItem, PersonalItemCreated, PersonalItemSourceType, PersonalKnowledgeScope, PersonalPage, PersonalPublishRequest, PersonalSession, PersonalSessionDetail, PersonalSpace, ServiceMemory } from '@/types/api'; import type { CaptureStatusHistory } from '@/types/api'; import { apiRequest, apiUrl, authHeaders, readTextPayload } from './api'; import { personalItemApiPath, personalSessionApiPath } from './personal-assistant-id'; const candidateBase = '/api/aihr/personal-assistant/memory-candidates'; type RawServiceMemory = Omit & { version?: number; currentVersion?: number }; const rowsOf = (value: T[] | { rows?: T[]; records?: T[] } | null | undefined): T[] => { if (Array.isArray(value)) return value; return value?.rows || value?.records || []; }; export const createMemoryIdempotencyKey = (candidateId: number) => { const suffix = typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function' ? crypto.randomUUID() : `${Date.now()}-${Math.random().toString(36).slice(2, 12)}`; return `memory-confirm-${candidateId}-${suffix}`; }; export const getMemoryCandidates = async (status = 'DRAFT') => rowsOf(await apiRequest({ url: `${candidateBase}?status=${encodeURIComponent(status)}`, method: 'GET', timeout: 15000 })); export const confirmMemoryCandidate = (candidateId: number, request: MemoryConfirmRequest) => apiRequest({ url: `${candidateBase}/${candidateId}/confirm`, method: 'POST', data: request, timeout: 15000 }); export const dismissMemoryCandidate = (candidateId: number) => apiRequest<{ id: number; status: string }>({ url: `${candidateBase}/${candidateId}/dismiss`, method: 'POST', timeout: 15000 }); export const getAssistantCaptures = async () => rowsOf(await apiRequest({ url: '/api/aihr/personal-assistant/assistant-captures?limit=50', method: 'GET', timeout: 15000 })); export const updateAssistantCaptureStatus = ( captureId: number, businessStatus: 'RECORDED' | 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'VOID', note = '' ) => apiRequest({ url: `/api/aihr/personal-assistant/assistant-captures/${captureId}/status`, method: 'POST', data: { businessStatus, note, idempotencyKey: `capture-status-${captureId}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}` }, timeout: 15000 }); export const getAssistantCaptureStatusHistory = (captureId: number) => apiRequest({ url: `/api/aihr/personal-assistant/assistant-captures/${captureId}/status-history`, method: 'GET', timeout: 15000 }); export const assistantCaptureSourceUrl = (captureId: number) => apiUrl(`/api/aihr/personal-assistant/assistant-captures/${captureId}/source`); export const getServiceMemories = async () => { const rows = rowsOf(await apiRequest({ url: '/api/aihr/service-memories', method: 'GET', timeout: 15000 })); return rows.map((item) => ({ ...item, version: item.version ?? item.currentVersion ?? 0 })); }; export type KnowledgeScope = PersonalKnowledgeScope; export interface PersonalItemQuery { pageNum?: number; pageSize?: number; status?: string; sourceType?: PersonalItemSourceType | ''; dateFrom?: string; dateTo?: string; keyword?: string; } const personalQuery = (query: PersonalItemQuery) => { const entries = Object.entries(query).filter(([, value]) => value !== undefined && value !== ''); return entries.length ? `?${entries.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`).join('&')}` : ''; }; export const askPersonalAssistant = (data: { sessionId?: string; queryText: string; scope: KnowledgeScope[]; dateFrom?: string; dateTo?: string; itemIds?: string[]; outputFormat?: 'ANSWER' | 'ACTION_PLAN' | 'OUTLINE'; }) => apiRequest({ url: '/api/aihr/personal-assistant/ask', method: 'POST', data, timeout: 45000 }); export const mergePersonalRows = (current: PersonalItem[], incoming: PersonalItem[]) => Array.from(new Map([...current, ...incoming].map((item) => [item.id, item])).values()); export const loadPersonalSpace = () => apiRequest({ url: '/api/aihr/personal-assistant/space', method: 'GET' }); export const loadPersonalItems = (query: PersonalItemQuery = {}) => apiRequest>({ url: `/api/aihr/personal-assistant/items${personalQuery(query)}`, method: 'GET' }); export const loadPersonalItem = (id: string) => apiRequest({ url: personalItemApiPath(id), method: 'GET' }); export const createTextItem = (data: { title?: string; content: string; capturedAt?: string; tags?: string[] }) => apiRequest({ url: '/api/aihr/personal-assistant/items/text', method: 'POST', data }); export const createUrlItem = (data: { url: string; title?: string; capturedAt?: string }) => apiRequest({ url: '/api/aihr/personal-assistant/items/url', method: 'POST', data, timeout: 45000 }); export interface PersonalSelectedFile { path: string; name: string; size: number; } export const choosePersonalFile = () => new Promise((resolve, reject) => { uni.chooseFile({ count: 1, type: 'all', success: (result) => { const file = (Array.isArray(result.tempFiles) ? result.tempFiles[0] : undefined) as Partial | undefined; const path = file?.path || result.tempFilePaths?.[0] || ''; if (!path) return reject(new Error('未获取到文件路径')); resolve({ path, name: file?.name || path.split('/').pop() || 'personal-file', size: Number(file?.size || 0) }); }, fail: () => reject(new Error('未选择文件')) }); }); export const uploadPersonalFile = (file: PersonalSelectedFile, title?: string, capturedAt?: string) => new Promise((resolve, reject) => uni.uploadFile({ url: apiUrl('/api/aihr/personal-assistant/items/file'), filePath: file.path, name: 'file', header: authHeaders(false), formData: { ...(title?.trim() ? { title: title.trim() } : {}), ...(capturedAt ? { capturedAt } : {}) }, timeout: 45000, success: (response) => { try { resolve(readTextPayload(response.statusCode, response.data)); } catch (error) { reject(error); } }, fail: () => reject(new Error('文件收藏失败')) })); export const retryPersonalItem = (id: string) => apiRequest({ url: personalItemApiPath(id, '/retry'), method: 'POST' }); export const retryPersonalOcrPages = (id: string) => apiRequest({ url: personalItemApiPath(id, '/ocr/retry-failed'), method: 'POST' }); export const deletePersonalItem = (id: string) => apiRequest<{ cleanupJobId: string }>({ url: personalItemApiPath(id), method: 'DELETE', timeout: 15000 }); export const downloadPersonalItem = (id: string) => apiRequest<{ url: string; expiresAt?: string }>({ url: personalItemApiPath(id, '/download-url'), method: 'GET' }); export const loadPersonalSessions = () => apiRequest({ url: '/api/aihr/personal-assistant/sessions', method: 'GET' }); export const loadPersonalSession = (id: string) => apiRequest({ url: personalSessionApiPath(id), method: 'GET' }); export const deletePersonalSession = (id: string) => apiRequest({ url: personalSessionApiPath(id), method: 'DELETE' }); export const createPersonalOutline = (data: { sessionId: string; title?: string }) => apiRequest({ url: '/api/aihr/personal-assistant/exports/outline', method: 'POST', data }); export const updatePersonalOutline = (id: string, data: { title: string; sections: PersonalExportOutlineSection[] }) => apiRequest({ url: `/api/aihr/personal-assistant/exports/${encodeURIComponent(id)}/outline`, method: 'PUT', data }); export const confirmPersonalOutline = (id: string) => apiRequest({ url: `/api/aihr/personal-assistant/exports/${encodeURIComponent(id)}/confirm`, method: 'POST' }); export const loadPersonalExport = (id: string) => apiRequest({ url: `/api/aihr/personal-assistant/exports/${encodeURIComponent(id)}`, method: 'GET' }); export const enqueuePersonalPpt = (id: string) => apiRequest({ url: `/api/aihr/personal-assistant/exports/${encodeURIComponent(id)}/pptx`, method: 'POST', data: { templateCode: 'standard' } }); export const downloadPersonalExport = (id: string) => apiRequest<{ url: string; expiresAt?: string }>({ url: `/api/aihr/personal-assistant/exports/${encodeURIComponent(id)}/download-url`, method: 'GET' }); export const requestPersonalPublish = (itemId: string, reason: string) => apiRequest({ url: `${personalItemApiPath(itemId)}/publish-requests`, method: 'POST', data: { targetScope: 'POSITION:生活顾问', reason } }); export const loadPersonalPublishRequests = () => apiRequest({ url: '/api/aihr/personal-assistant/publish-requests', method: 'GET' });