全新出发
This commit is contained in:
1 parent
b06c117b1e
commit
fa4edebed6
463 files changed
+181610
-125
No files matched your search
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* ExportV2 — useExportTasks hook
|
||||
*
|
||||
* Manages the queue and state of manual export tasks.
|
||||
* Includes starting tasks via electronAPI, tracking progress, and aborting.
|
||||
*/
|
||||
|
||||
import { useState, useCallback, useRef, useEffect } from 'react'
|
||||
import type { ExportTask, ExportTaskPayload, TaskStatus } from '../types'
|
||||
import { createEmptyProgress } from '../constants'
|
||||
import { useExportTaskStore } from '../../../stores/exportTaskStore'
|
||||
import { buildProgressPayloadSignature } from '../utils/progress'
|
||||
import { resolvePerfStageByPhase, applyProgressToTaskPerformance } from '../utils/performance'
|
||||
import type { ExportProgress } from '../types'
|
||||
|
||||
export interface ExportTasksResult {
|
||||
tasks: ExportTask[]
|
||||
activeTasks: ExportTask[]
|
||||
completedTasks: ExportTask[]
|
||||
startTask: (payload: ExportTaskPayload) => void
|
||||
cancelTask: (taskId: string) => void
|
||||
clearCompletedTasks: () => void
|
||||
}
|
||||
|
||||
const generateTaskId = () => `task-${Date.now()}-${Math.random().toString(36).substr(2, 6)}`
|
||||
|
||||
export function useExportTasks(): ExportTasksResult {
|
||||
const [tasks, setTasks] = useState<ExportTask[]>([])
|
||||
|
||||
const tasksRef = useRef<ExportTask[]>([])
|
||||
tasksRef.current = tasks
|
||||
|
||||
const { setSessionStatus } = useExportTaskStore()
|
||||
|
||||
// Track the ongoing tasks to update the global zustand store badge
|
||||
useEffect(() => {
|
||||
const activeTasks = tasksRef.current.filter(t => t.status === 'running' || t.status === 'cancel_requested')
|
||||
const inProgressSessionIds = new Set<string>()
|
||||
activeTasks.forEach(task => {
|
||||
task.payload.sessionIds.forEach(id => inProgressSessionIds.add(id))
|
||||
})
|
||||
|
||||
setSessionStatus({
|
||||
activeTaskCount: activeTasks.length,
|
||||
inProgressSessionIds: Array.from(inProgressSessionIds)
|
||||
})
|
||||
}, [tasks, setSessionStatus])
|
||||
|
||||
const updateTask = useCallback((taskId: string, updater: (prev: ExportTask) => ExportTask) => {
|
||||
setTasks(prev => prev.map(t => t.id === taskId ? updater(t) : t))
|
||||
}, [])
|
||||
|
||||
const startTask = useCallback((payload: ExportTaskPayload) => {
|
||||
const taskId = generateTaskId()
|
||||
const title = payload.sessionIds.length > 1
|
||||
? `批量导出 ${payload.sessionIds.length} 个会话`
|
||||
: `导出 ${payload.sessionNames[0] || '会话'}`
|
||||
|
||||
const newTask: ExportTask = {
|
||||
id: taskId,
|
||||
title,
|
||||
status: 'running',
|
||||
createdAt: Date.now(),
|
||||
startedAt: Date.now(),
|
||||
payload,
|
||||
progress: createEmptyProgress()
|
||||
}
|
||||
|
||||
setTasks(prev => [newTask, ...prev])
|
||||
|
||||
// Kick off via electron API
|
||||
const run = async () => {
|
||||
let progressUnsubscribe: (() => void) | null = null
|
||||
let lastProgressSig = ''
|
||||
|
||||
try {
|
||||
// Subscribe to progress
|
||||
progressUnsubscribe = window.electronAPI.export.onProgress((progressPayload: ExportProgress) => {
|
||||
if ((progressPayload as any).taskId && (progressPayload as any).taskId !== taskId) return
|
||||
|
||||
const sig = buildProgressPayloadSignature(progressPayload)
|
||||
if (sig === lastProgressSig) return
|
||||
lastProgressSig = sig
|
||||
|
||||
updateTask(taskId, (task) => {
|
||||
if (task.status !== 'running') return task
|
||||
|
||||
const nextProgress = { ...task.progress, ...progressPayload }
|
||||
// The original expected performance from task or undefined.
|
||||
// The signature is applyProgressToTaskPerformance(task: ExportTask, payload: ExportProgress, now: number)
|
||||
const nextPerf = applyProgressToTaskPerformance(task, progressPayload, Date.now())
|
||||
|
||||
return {
|
||||
...task,
|
||||
progress: nextProgress,
|
||||
performance: nextPerf
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const frontendOptions = payload.options as any
|
||||
const electronOptions = { ...frontendOptions }
|
||||
if (frontendOptions?.useAllTime) {
|
||||
electronOptions.dateRange = null
|
||||
} else if (frontendOptions?.dateRange) {
|
||||
electronOptions.dateRange = {
|
||||
start: frontendOptions.dateRange.start.getTime(),
|
||||
end: frontendOptions.dateRange.end.getTime()
|
||||
}
|
||||
}
|
||||
|
||||
const result = await window.electronAPI.export.exportSessions(
|
||||
payload.sessionIds,
|
||||
payload.outputDir,
|
||||
electronOptions,
|
||||
{ taskId }
|
||||
)
|
||||
|
||||
updateTask(taskId, (task) => ({
|
||||
...task,
|
||||
status: result?.success ? 'success' : 'error',
|
||||
finishedAt: Date.now(),
|
||||
error: result?.error || undefined,
|
||||
sessionOutputPaths: result?.sessionOutputPaths
|
||||
}))
|
||||
|
||||
} catch (err: any) {
|
||||
console.error('[useExportTasks] Task failed:', err)
|
||||
updateTask(taskId, (task) => ({
|
||||
...task,
|
||||
status: 'error',
|
||||
finishedAt: Date.now(),
|
||||
error: err.message || '未知错误'
|
||||
}))
|
||||
} finally {
|
||||
if (progressUnsubscribe) {
|
||||
progressUnsubscribe()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void run()
|
||||
}, [updateTask])
|
||||
|
||||
const cancelTask = useCallback((taskId: string) => {
|
||||
updateTask(taskId, (task) => {
|
||||
if (task.status === 'running') {
|
||||
window.electronAPI.export.cancelTask(taskId)
|
||||
return { ...task, status: 'cancel_requested' }
|
||||
}
|
||||
return task
|
||||
})
|
||||
}, [updateTask])
|
||||
|
||||
const clearCompletedTasks = useCallback(() => {
|
||||
setTasks(prev => prev.filter(t => t.status === 'running' || t.status === 'cancel_requested'))
|
||||
}, [])
|
||||
|
||||
const activeTasks = tasks.filter(t => t.status === 'running' || t.status === 'cancel_requested')
|
||||
const completedTasks = tasks.filter(t => t.status === 'success' || t.status === 'error')
|
||||
|
||||
return {
|
||||
tasks,
|
||||
activeTasks,
|
||||
completedTasks,
|
||||
startTask,
|
||||
cancelTask,
|
||||
clearCompletedTasks
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user