初始化模版工程

This commit is contained in:
Cloud Bot
2026-03-20 07:33:46 +00:00
commit 23717e0ecd
386 changed files with 51675 additions and 0 deletions

View File

@@ -0,0 +1,232 @@
import { useEffect, useMemo, useState } from 'react'
import { useRequest } from 'ahooks'
import { useNovaEvents } from './useNovaEvents'
import { useEventStore } from '../store/useEventStore'
import { useArtifactsExtractor } from './useArtifactsExtractor'
import { useEventProcessor } from './useEventProcessor'
import { usePanelState } from './usePanelState'
import { useAttachmentHandlers } from './useAttachmentHandlers'
import { useMessageSender } from './useMessageSender'
import { useNovaService } from './useNovaService'
import type { ConversationInfo } from './useNovaService'
import { TERMINAL_TASK_STATUS, TaskStatus } from '../types'
import type { PlatformConfig, ApiEvent } from './useNovaEvents'
export interface UseNovaChatLogicProps {
mode: 'chat' | 'share'
conversationId?: string
platformConfig?: PlatformConfig
reconnectLimit?: number
reconnectInterval?: number
agentId?: string
getToken?: () => string | undefined
getTenantId?: () => string | undefined
onEvent?: (event: ApiEvent) => void
onConnectionChange?: (connected: boolean) => void
onError?: (error: Error) => void
}
/**
* Nova Chat 主业务逻辑 Hook
* 采用原子化模块组合模式,提高代码鲁棒性和可维护性
*/
export function useNovaChatLogic({
mode,
conversationId,
platformConfig,
reconnectLimit = 3,
reconnectInterval = 3000,
getToken,
getTenantId,
onEvent,
onConnectionChange,
onError,
agentId,
}: UseNovaChatLogicProps) {
// 1. 核心事件与连接管理
const novaEvents = useNovaEvents({
mode,
conversationId,
platformConfig: platformConfig || { wssUrl: '', apiBaseUrl: '', agentId: '', agentName: '' },
reconnectLimit,
reconnectInterval,
getToken,
getTenantId,
onEvent,
onConnectionChange,
onError,
})
const { rawEvents } = novaEvents
// 2. 数据处理与提取
const { artifacts, taskStatus: eventTaskStatus } = useArtifactsExtractor(rawEvents)
const [polledTaskStatus, setPolledTaskStatus] = useState<string | undefined>()
const [initialTaskStatusLoading, setInitialTaskStatusLoading] = useState(false)
const taskStatus = useMemo(
() => (polledTaskStatus as typeof eventTaskStatus | undefined) || eventTaskStatus,
[polledTaskStatus, eventTaskStatus],
)
const processedMessages = useEventProcessor(rawEvents)
// 3. 面板与附件状态管理
const {
panelVisible,
selectedAttachment,
togglePanel,
closePanel,
selectAttachment
} = usePanelState()
// 4. 附件操作处理器
const {
handleAttachmentClick,
handleImageAttachmentClick,
handleToolCallClick
} = useAttachmentHandlers(selectAttachment)
// 5. 消息发送逻辑
const { sendingMessage, handleSend, setSendingMessage } = useMessageSender({
conversationId,
platformConfig,
sendMessage: novaEvents.sendMessage,
agentId,
})
// 6. 工件服务URL 获取等)
const service = useNovaService({
platformConfig,
getToken,
getTenantId,
conversationId
})
const { getConversationInfoList } = service
// 7. Store 同步(副作用管理)
const setEvents = useEventStore((state) => state.setEvents)
const setArtifacts = useEventStore((state) => state.setArtifacts)
// 8. WS 阶段轮询会话信息ahooks useRequest遇到终止状态时关闭 loading
const terminalSet = new Set(TERMINAL_TASK_STATUS.map((s) => String(s).toLowerCase()))
const getConversationTaskStatus = (
list: ConversationInfo[] | undefined,
currentConversationId?: string,
) => {
if (!currentConversationId) return undefined
return list?.find(
item => item.conversation_id === currentConversationId,
)?.task_status
}
const isTerminalTaskStatus = (status?: string) =>
!!status && terminalSet.has(String(status).toLowerCase())
const shouldPollTaskStatus =
!!conversationId &&
(sendingMessage || taskStatus === TaskStatus.IN_PROGRESS)
const stopChat = async () => {
await service.stopChat()
setSendingMessage(false)
setPolledTaskStatus(TaskStatus.STOPPED)
cancelPoll()
}
const { run: runPoll, cancel: cancelPoll } = useRequest(
(cid: string) =>
getConversationInfoList([cid]).then((res) => res.data ?? []),
{
manual: true,
pollingInterval: shouldPollTaskStatus ? 5000 : undefined,
ready: shouldPollTaskStatus,
onSuccess: (list) => {
const nextTaskStatus = getConversationTaskStatus(list, conversationId)
if (nextTaskStatus) {
setPolledTaskStatus(nextTaskStatus)
}
if (isTerminalTaskStatus(nextTaskStatus)) {
setSendingMessage(false)
cancelPoll()
}
},
}
)
useEffect(() => {
if (shouldPollTaskStatus && conversationId) {
runPoll(conversationId)
} else {
cancelPoll()
}
}, [shouldPollTaskStatus, conversationId, runPoll, cancelPoll])
useEffect(() => {
let cancelled = false
setPolledTaskStatus(undefined)
if (!conversationId) {
setInitialTaskStatusLoading(false)
return
}
setInitialTaskStatusLoading(true)
void getConversationInfoList([conversationId])
.then((res) => {
if (cancelled) return
const nextTaskStatus = getConversationTaskStatus(res.data ?? [], conversationId)
if (nextTaskStatus) {
setPolledTaskStatus(nextTaskStatus)
}
})
.catch(() => {})
.finally(() => {
if (!cancelled) {
setInitialTaskStatusLoading(false)
}
})
return () => {
cancelled = true
}
}, [conversationId, getConversationInfoList])
useEffect(() => {
setEvents(rawEvents)
}, [rawEvents, setEvents])
useEffect(() => {
setArtifacts(artifacts)
}, [artifacts, setArtifacts])
// 9. 统一对外接口
return {
// 状态数据
messages: processedMessages,
loading: sendingMessage || initialTaskStatusLoading,
taskStatus,
artifacts,
hasArtifacts: artifacts.length > 0,
panelVisible,
selectedAttachment,
// 操作方法
handleSend,
handlePanelToggle: togglePanel,
handlePanelClose: closePanel,
handleAttachmentClick,
handleImageAttachmentClick,
handleToolCallClick,
setLoading: setSendingMessage,
agentId,
// 统一的 API 命名空间
api: {
...service,
stopChat,
}
}
}