import { reactive, ref, type Ref } from 'vue' import { ElMessage } from 'element-plus' import { generateDataProcess, getDataProcessProgress, getDataProcessResults, restoreDataProcessResult, stopDataProcess, updateDataProcessResult, type DataProcessProgress, type DataProcessResult, } from '@/api/modules/dataProcess' import type { GenerationState, ResultItem } from './types' interface GenerationBindings { taskId: Ref dirty: Ref beforeGenerate?: () => Promise } const RESULT_PAGE_SIZE = 500 const POLL_INTERVAL_MS = 1500 function mapResult(item: DataProcessResult): ResultItem { return { id: String(item.id), previewItemId: item.preview_item_id == null ? null : String(item.preview_item_id), instruction: item.instruction, input: item.input || '', output: item.output, originalInstruction: item.original_instruction ?? item.instruction, originalInput: item.original_input ?? item.input ?? '', originalOutput: item.original_output ?? item.output, status: item.status, error: item.error || undefined, split: item.split || undefined, qualityScore: item.quality_score?.overall, qualityFlags: item.quality_score?.flags || [], updatedAt: item.updated_at, } } export function useDataProcessGeneration(bindings: GenerationBindings) { const results = ref([]) const selectedResultId = ref(null) const generation = reactive({ status: 'idle', progress: 0, message: '确认摘要后即可开始生成,过程中可查看实时进度。', }) let generationTimer: ReturnType | null = null let generationRun = 0 let pollFailureCount = 0 function stopGenerationTimer() { generationRun += 1 if (generationTimer) clearTimeout(generationTimer) generationTimer = null } function resetDownstream() { stopGenerationTimer() generation.status = 'idle' generation.progress = 0 generation.message = '确认摘要后即可开始生成,过程中可查看实时进度。' results.value = [] selectedResultId.value = null } function applyProgress(progress: DataProcessProgress) { generation.progress = Math.max(0, Math.min(100, Number(progress.progress) || 0)) generation.message = progress.message || ( progress.status === 'running' ? '后端正在生成标准化结果并进行质量评分。' : progress.status === 'completed' ? '数据处理已完成。' : progress.failure_reason || '任务已停止。' ) } async function loadAllResults(taskId: string) { const first = await getDataProcessResults(taskId, { page: 1, page_size: RESULT_PAGE_SIZE }) const items = [...first.items] const pages = Math.ceil(first.total / first.page_size) for (let page = 2; page <= pages; page += 1) { const next = await getDataProcessResults(taskId, { page, page_size: RESULT_PAGE_SIZE }) items.push(...next.items) } results.value = items.map(mapResult) selectedResultId.value = results.value[0]?.id ?? null } async function finishFromProgress(progress: DataProcessProgress) { pollFailureCount = 0 applyProgress(progress) if (progress.status === 'completed') { const taskId = bindings.taskId.value if (!taskId) return await loadAllResults(taskId) generation.status = 'success' generation.progress = 100 generation.message = `已完成 ${results.value.length.toLocaleString()} 条数据处理,可进入结果页检查。` bindings.dirty.value = true ElMessage.success('数据处理完成') return } if (progress.status === 'failed' || progress.status === 'stopped') { generation.status = 'failed' generation.message = progress.failure_reason || progress.message || ( progress.status === 'stopped' ? '任务已停止,可以重新生成。' : '数据处理失败,请检查配置后重试。' ) } } async function pollGeneration(runId: number) { const taskId = bindings.taskId.value if (!taskId || runId !== generationRun || generation.status !== 'running') return try { const progress = await getDataProcessProgress(taskId) if (runId !== generationRun) return pollFailureCount = 0 if (progress.status === 'running' || progress.status === 'pending') { applyProgress(progress) generationTimer = setTimeout(() => void pollGeneration(runId), POLL_INTERVAL_MS) return } await finishFromProgress(progress) } catch (error) { if (runId !== generationRun) return pollFailureCount += 1 if (pollFailureCount <= 3) { generation.message = `进度查询暂时失败,正在重试(${pollFailureCount}/3)…` generationTimer = setTimeout(() => void pollGeneration(runId), POLL_INTERVAL_MS) return } generation.status = 'failed' generation.message = error instanceof Error ? error.message : '查询任务进度失败,请重试。' } } async function startGeneration() { const taskId = bindings.taskId.value if (!taskId) { ElMessage.error('任务尚未创建,请返回上一步重试') return } stopGenerationTimer() const runId = generationRun generation.status = 'running' pollFailureCount = 0 generation.progress = 0 generation.message = '正在同步预览修改并启动后端处理,请稍候。' try { await bindings.beforeGenerate?.() const progress = await generateDataProcess(taskId) if (runId !== generationRun) return if (progress.status === 'completed' || progress.status === 'failed' || progress.status === 'stopped') { await finishFromProgress(progress) return } applyProgress(progress) generationTimer = setTimeout(() => void pollGeneration(runId), POLL_INTERVAL_MS) } catch (error) { if (runId !== generationRun) return generation.status = 'failed' generation.message = error instanceof Error ? error.message : '启动数据处理失败,请重试。' } } async function stopGeneration() { const taskId = bindings.taskId.value if (!taskId) return stopGenerationTimer() try { const progress = await stopDataProcess(taskId) applyProgress(progress) } catch { generation.status = 'running' generation.message = '停止请求失败,继续查询后端任务状态。' const runId = generationRun generationTimer = setTimeout(() => void pollGeneration(runId), POLL_INTERVAL_MS) return } generation.status = 'failed' generation.message = '任务已停止,预览修改仍然保留,可以重新生成。' } function updateResultField(id: string, field: 'instruction' | 'input' | 'output', value: string) { const item = results.value.find((entry) => entry.id === id) if (!item) return item[field] = value const valid = item.instruction.trim() && item.output.trim() item.error = valid ? undefined : 'Instruction 和 Output 不能为空' const changed = item.instruction !== item.originalInstruction || item.input !== item.originalInput || item.output !== item.originalOutput item.status = item.error ? 'invalid' : changed ? 'modified' : 'valid' bindings.dirty.value = true } async function restoreResult(id: string) { const taskId = bindings.taskId.value const item = results.value.find((entry) => entry.id === id) if (!taskId || !item) return const restored = await restoreDataProcessResult(taskId, id) const index = results.value.indexOf(item) results.value[index] = mapResult(restored) bindings.dirty.value = true } async function persistResultChanges() { const taskId = bindings.taskId.value if (!taskId) throw new Error('任务尚未创建') const changed = results.value.filter((item) => ( item.instruction !== item.originalInstruction || item.input !== item.originalInput || item.output !== item.originalOutput )) for (const item of changed) { const saved = await updateDataProcessResult(taskId, item.id, { instruction: item.instruction, input: item.input, output: item.output, expected_updated_at: item.updatedAt, }) const index = results.value.findIndex((entry) => entry.id === item.id) if (index >= 0) results.value[index] = mapResult(saved) } } function validateResults() { let firstInvalidId: string | null = null for (const item of results.value) { if (!item.instruction.trim() || !item.output.trim() || item.status === 'invalid') { item.error ||= '结果未通过后端质量校验,请修改后重新保存' item.status = 'invalid' firstInvalidId ??= item.id } } if (firstInvalidId) selectedResultId.value = firstInvalidId return firstInvalidId == null } return { generation, results, selectedResultId, persistResultChanges, resetDownstream, restoreResult, startGeneration, stopGeneration, stopGenerationTimer, updateResultField, validateResults, } }