From fdb6d8f6484e5eb93c20965647c092c5477e615e Mon Sep 17 00:00:00 2001 From: digua Date: Sat, 30 May 2026 12:56:09 +0800 Subject: [PATCH] feat(import): update demo data sources to 4 files Switch from 2 demo files (group + private) to 4 files: - demo-group.json - demo-private-A-cuilan.json - demo-private-B-wukong.json - demo-private-C-spider.json Update DemoImportResult.privateSessionId to privateSessionIds array to support multiple private chat demos. Co-authored-by: Cursor --- apps/cli/src/http/routes/web/import.ts | 54 +++++++++++-------- apps/desktop/main/ipc/demo.ts | 51 ++++++++++-------- apps/desktop/preload/apis/chat.ts | 2 +- apps/desktop/preload/index.d.ts | 2 +- .../home/components/DemoImportButton.vue | 4 +- src/services/import/types.ts | 2 +- 6 files changed, 65 insertions(+), 50 deletions(-) diff --git a/apps/cli/src/http/routes/web/import.ts b/apps/cli/src/http/routes/web/import.ts index 571efddd..9ec6a4dc 100644 --- a/apps/cli/src/http/routes/web/import.ts +++ b/apps/cli/src/http/routes/web/import.ts @@ -307,9 +307,17 @@ export function registerImportRoutes(server: FastifyInstance, dbManager: Databas // ==================== Demo Import ==================== + const DEMO_FILES = [ + 'demo-group.json', + 'demo-private-A-cuilan.json', + 'demo-private-B-wukong.json', + 'demo-private-C-spider.json', + ] + server.post<{ Body: { locale?: string } }>('/_web/demo/import', async (request, reply) => { const locale = (request.body as any)?.locale || 'en' const nativeBinding = resolveNativeBinding() + const total = DEMO_FILES.length reply.raw.writeHead(200, { 'Content-Type': 'text/event-stream', @@ -322,41 +330,41 @@ export function registerImportRoutes(server: FastifyInstance, dbManager: Databas } const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'chatlab-demo-')) - const groupPath = path.join(tmpDir, 'demo-group.json') - const privatePath = path.join(tmpDir, 'demo-private.json') try { - sendEvent('progress', { stage: 'downloading', current: 1, total: 2 }) - const groupResp = await fetch(`${DEMO_BASE_URL}/${locale}/demo-group.json`, { - signal: AbortSignal.timeout(60_000), - }) - if (!groupResp.ok) throw new Error(`Download group demo failed: ${groupResp.status}`) - fs.writeFileSync(groupPath, Buffer.from(await groupResp.arrayBuffer())) + const localPaths: string[] = [] + for (let i = 0; i < total; i++) { + sendEvent('progress', { stage: 'downloading', current: i + 1, total }) + const localPath = path.join(tmpDir, DEMO_FILES[i]) + const resp = await fetch(`${DEMO_BASE_URL}/${locale}/${DEMO_FILES[i]}`, { + signal: AbortSignal.timeout(60_000), + }) + if (!resp.ok) throw new Error(`Download demo failed (${DEMO_FILES[i]}): ${resp.status}`) + fs.writeFileSync(localPath, Buffer.from(await resp.arrayBuffer())) + localPaths.push(localPath) + } - sendEvent('progress', { stage: 'downloading', current: 2, total: 2 }) - const privateResp = await fetch(`${DEMO_BASE_URL}/${locale}/demo-private.json`, { - signal: AbortSignal.timeout(60_000), - }) - if (!privateResp.ok) throw new Error(`Download private demo failed: ${privateResp.status}`) - fs.writeFileSync(privatePath, Buffer.from(await privateResp.arrayBuffer())) - - sendEvent('progress', { stage: 'importing', current: 1, total: 2 }) - const groupResult = await streamImport(dbManager, groupPath, { nativeBinding }) + sendEvent('progress', { stage: 'importing', current: 1, total }) + const groupResult = await streamImport(dbManager, localPaths[0], { nativeBinding }) if (!groupResult.success) throw new Error(groupResult.error || 'Failed to import group demo') - sendEvent('progress', { stage: 'importing', current: 2, total: 2 }) - const privateResult = await streamImport(dbManager, privatePath, { nativeBinding }) - if (!privateResult.success) throw new Error(privateResult.error || 'Failed to import private demo') + const privateSessionIds: string[] = [] + for (let i = 1; i < localPaths.length; i++) { + sendEvent('progress', { stage: 'importing', current: i + 1, total }) + const result = await streamImport(dbManager, localPaths[i], { nativeBinding }) + if (!result.success) throw new Error(result.error || `Failed to import private demo: ${DEMO_FILES[i]}`) + if (result.sessionId) privateSessionIds.push(result.sessionId) + } - sendEvent('progress', { stage: 'done', current: 2, total: 2 }) + sendEvent('progress', { stage: 'done', current: total, total }) sendEvent('result', { success: true, groupSessionId: groupResult.sessionId, - privateSessionId: privateResult.sessionId, + privateSessionIds, }) } catch (err) { const message = err instanceof Error ? err.message : String(err) - sendEvent('progress', { stage: 'error', current: 0, total: 2, message }) + sendEvent('progress', { stage: 'error', current: 0, total, message }) sendEvent('result', { success: false, error: message }) } finally { reply.raw.end() diff --git a/apps/desktop/main/ipc/demo.ts b/apps/desktop/main/ipc/demo.ts index 3e793d76..ee3b521e 100644 --- a/apps/desktop/main/ipc/demo.ts +++ b/apps/desktop/main/ipc/demo.ts @@ -60,6 +60,13 @@ export function registerDemoHandlers(ctx: IpcContext): void { * 下载并导入 Demo 示例数据 * 返回群聊和私聊的 sessionId */ + const DEMO_FILES = [ + 'demo-group.json', + 'demo-private-A-cuilan.json', + 'demo-private-B-wukong.json', + 'demo-private-C-spider.json', + ] + ipcMain.handle( 'demo:downloadAndImport', async ( @@ -68,52 +75,52 @@ export function registerDemoHandlers(ctx: IpcContext): void { ): Promise<{ success: boolean groupSessionId?: string - privateSessionId?: string + privateSessionIds?: string[] error?: string }> => { const tempDir = getDemoTempDir() - const groupPath = path.join(tempDir, 'demo-group.json') - const privatePath = path.join(tempDir, 'demo-private.json') + const total = DEMO_FILES.length const sendProgress = (progress: DemoProgress) => { win.webContents.send('demo:progress', progress) } try { - // Phase 1: Download - sendProgress({ stage: 'downloading', current: 1, total: 2 }) - await downloadFile(`${DEMO_BASE_URL}/${locale}/demo-group.json`, groupPath) - - sendProgress({ stage: 'downloading', current: 2, total: 2 }) - await downloadFile(`${DEMO_BASE_URL}/${locale}/demo-private.json`, privatePath) - - // Phase 2: Import group chat - sendProgress({ stage: 'importing', current: 1, total: 2 }) - const groupResult = await worker.streamImport(groupPath) + const localPaths: string[] = [] + for (let i = 0; i < total; i++) { + sendProgress({ stage: 'downloading', current: i + 1, total }) + const localPath = path.join(tempDir, DEMO_FILES[i]) + await downloadFile(`${DEMO_BASE_URL}/${locale}/${DEMO_FILES[i]}`, localPath) + localPaths.push(localPath) + } + sendProgress({ stage: 'importing', current: 1, total }) + const groupResult = await worker.streamImport(localPaths[0]) if (!groupResult.success || !groupResult.sessionId) { throw new Error(groupResult.error || 'Failed to import group demo') } - // Phase 3: Import private chat - sendProgress({ stage: 'importing', current: 2, total: 2 }) - const privateResult = await worker.streamImport(privatePath) - - if (!privateResult.success || !privateResult.sessionId) { - throw new Error(privateResult.error || 'Failed to import private demo') + const privateSessionIds: string[] = [] + for (let i = 1; i < localPaths.length; i++) { + sendProgress({ stage: 'importing', current: i + 1, total }) + const result = await worker.streamImport(localPaths[i]) + if (!result.success || !result.sessionId) { + throw new Error(result.error || `Failed to import private demo: ${DEMO_FILES[i]}`) + } + privateSessionIds.push(result.sessionId) } - sendProgress({ stage: 'done', current: 2, total: 2 }) + sendProgress({ stage: 'done', current: total, total }) return { success: true, groupSessionId: groupResult.sessionId, - privateSessionId: privateResult.sessionId, + privateSessionIds, } } catch (error) { const message = error instanceof Error ? error.message : String(error) console.error('[Demo] Download and import failed:', message) - sendProgress({ stage: 'error', current: 0, total: 2, message }) + sendProgress({ stage: 'error', current: 0, total, message }) return { success: false, error: message } } finally { cleanupDemoTemp(tempDir) diff --git a/apps/desktop/preload/apis/chat.ts b/apps/desktop/preload/apis/chat.ts index 395f3b84..d2c9037e 100644 --- a/apps/desktop/preload/apis/chat.ts +++ b/apps/desktop/preload/apis/chat.ts @@ -501,7 +501,7 @@ export const chatApi = { ): Promise<{ success: boolean groupSessionId?: string - privateSessionId?: string + privateSessionIds?: string[] error?: string }> => { return ipcRenderer.invoke('demo:downloadAndImport', locale) diff --git a/apps/desktop/preload/index.d.ts b/apps/desktop/preload/index.d.ts index 64323b10..6d020a00 100644 --- a/apps/desktop/preload/index.d.ts +++ b/apps/desktop/preload/index.d.ts @@ -198,7 +198,7 @@ interface ChatApi { importDemo: (locale: string) => Promise<{ success: boolean groupSessionId?: string - privateSessionId?: string + privateSessionIds?: string[] error?: string }> diff --git a/src/pages/home/components/DemoImportButton.vue b/src/pages/home/components/DemoImportButton.vue index 59f3d6be..fd2e5523 100644 --- a/src/pages/home/components/DemoImportButton.vue +++ b/src/pages/home/components/DemoImportButton.vue @@ -48,8 +48,8 @@ async function handleImport() { const gapThreshold = getSessionGapThreshold() const sessionIndexService = useSessionIndexService() await sessionIndexService.generate(result.groupSessionId, gapThreshold) - if (result.privateSessionId) { - await sessionIndexService.generate(result.privateSessionId, gapThreshold) + if (result.privateSessionIds?.length) { + await Promise.all(result.privateSessionIds.map((id) => sessionIndexService.generate(id, gapThreshold))) } } catch (e) { console.error('自动生成会话索引失败:', e) diff --git a/src/services/import/types.ts b/src/services/import/types.ts index 1506747a..dd196e88 100644 --- a/src/services/import/types.ts +++ b/src/services/import/types.ts @@ -66,7 +66,7 @@ export interface DemoProgress { export interface DemoImportResult { success: boolean groupSessionId?: string - privateSessionId?: string + privateSessionIds?: string[] error?: string }