| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- 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[];
- } | null;
- candidates: NormalizedCandidate[];
- totalPool: number;
- error?: string;
- createdAt: string;
- updatedAt: string;
- }
- const tasks = new Map<string, Task>();
- 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<TaskStep>): 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<void> {
- 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<void> {
- return new Promise((resolve) => setTimeout(resolve, ms));
- }
|