import { analyzeBrief } from './llm.service.ts'; import { generateRecommendations, type NormalizedCandidate } from './recommendation.service.ts'; export type TaskStatus = 'pending' | 'uploading' | 'analyzing' | 'searching' | 'processing' | 'exporting' | 'completed' | 'failed'; export type StepStatus = 'pending' | 'running' | 'done' | 'error'; export interface TaskStep { id: string; label: string; status: StepStatus; progress: number; detail?: string; } export interface Task { id: string; status: TaskStatus; fileName: string; briefText: string; steps: TaskStep[]; requirements: Array<{ label: string; value: string; confidence: number }>; searchCriteria: { platforms: string[]; keywords: string[]; fanRange: { min: number; max: number }; budgetRange: { min: number; max: number }; region?: string; gender?: string; contentTags?: string[]; excludeTags?: string[]; targetCount?: number; } | null; candidates: NormalizedCandidate[]; totalPool: number; error?: string; createdAt: string; updatedAt: string; } const tasks = new Map(); function generateId(): string { return `task_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`; } function defaultSteps(): TaskStep[] { return [ { id: 'upload', label: '上传文件', status: 'pending', progress: 0 }, { id: 'analyze', label: 'AI 解析 Brief 需求', status: 'pending', progress: 0 }, { id: 'rules', label: '确定筛选规则', status: 'pending', progress: 0 }, { id: 'search', label: '搜索达人资源', status: 'pending', progress: 0 }, { id: 'recommend', label: '生成推荐名单', status: 'pending', progress: 0 }, { id: 'export', label: '准备导出', status: 'pending', progress: 0 }, ]; } export function createTask(fileName: string, briefText: string): Task { const task: Task = { id: generateId(), status: 'pending', fileName, briefText, steps: defaultSteps(), requirements: [], searchCriteria: null, candidates: [], totalPool: 0, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), }; tasks.set(task.id, task); return task; } export function getTask(taskId: string): Task | undefined { return tasks.get(taskId); } export function getAllTasks(): Task[] { return Array.from(tasks.values()); } function updateStep(task: Task, stepId: string, updates: Partial): void { const step = task.steps.find((s) => s.id === stepId); if (step) { Object.assign(step, updates); } task.updatedAt = new Date().toISOString(); } /** * 执行完整的提号流程 * Upload Brief → AI解析 → 规则确定 → 搜索达人 → 生成推荐 → 准备导出 */ export async function runTaskPipeline(taskId: string): Promise { const task = tasks.get(taskId); if (!task) { throw new Error(`Task not found: ${taskId}`); } try { // Step 1: Upload (already done) task.status = 'analyzing'; updateStep(task, 'upload', { status: 'done', progress: 100, detail: `已上传 ${task.fileName}` }); // Step 2: AI 解析 Brief updateStep(task, 'analyze', { status: 'running', progress: 20, detail: '正在解析 Brief 需求...' }); console.log(`[TaskPipeline] Brief 文本长度: ${task.briefText.length} 字符`); console.log(`[TaskPipeline] Brief 前200字: ${task.briefText.substring(0, 200)}...`); const analysisResult = await analyzeBrief(task.briefText); task.requirements = analysisResult.requirements; task.searchCriteria = analysisResult.searchCriteria; console.log(`[TaskPipeline] LLM 提取需求数: ${analysisResult.requirements.length}`); console.log(`[TaskPipeline] 搜索条件 platforms:`, analysisResult.searchCriteria.platforms); console.log(`[TaskPipeline] 搜索条件 keywords:`, analysisResult.searchCriteria.keywords); console.log(`[TaskPipeline] 搜索条件 fanRange:`, analysisResult.searchCriteria.fanRange); console.log(`[TaskPipeline] 搜索条件 budgetRange:`, analysisResult.searchCriteria.budgetRange); updateStep(task, 'analyze', { status: 'done', progress: 100, detail: `提取 ${analysisResult.requirements.length} 项需求,${analysisResult.searchCriteria.platforms.length} 个平台`, }); // Step 3: 确定规则 task.status = 'searching'; updateStep(task, 'rules', { status: 'running', progress: 50, detail: '应用默认筛选规则...' }); // 规则确定是自动的(使用默认规则) await delay(500); updateStep(task, 'rules', { status: 'done', progress: 100, detail: '已应用 5 条默认规则' }); // Step 4 & 5: 搜索达人 + 生成推荐 updateStep(task, 'search', { status: 'running', progress: 10, detail: '正在搜索达人资源...' }); const candidates = await generateRecommendations( task.searchCriteria, (stage, detail) => { if (stage === 'search') { updateStep(task, 'search', { status: 'running', progress: 50, detail }); } else if (stage === 'processing') { updateStep(task, 'search', { status: 'done', progress: 100, detail: '搜索完成' }); updateStep(task, 'recommend', { status: 'running', progress: 50, detail }); } } ); task.candidates = candidates; task.totalPool = candidates.length; console.log(`[TaskPipeline] 推荐引擎返回 ${candidates.length} 位候选达人`); updateStep(task, 'search', { status: 'done', progress: 100, detail: `召回 ${candidates.length} 位候选达人` }); updateStep(task, 'recommend', { status: 'done', progress: 100, detail: `强推荐 ${candidates.filter((c) => c.recommendStatus === '强推荐').length} 位,备选 ${candidates.filter((c) => c.recommendStatus === '备选').length} 位`, }); // Step 6: 准备导出 task.status = 'completed'; updateStep(task, 'export', { status: 'done', progress: 100, detail: '推荐表已准备就绪,可导出' }); } catch (error) { task.status = 'failed'; task.error = error instanceof Error ? error.message : '未知错误'; // Mark current running step as error for (const step of task.steps) { if (step.status === 'running') { step.status = 'error'; step.detail = task.error; } } } } function delay(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); }