Files
assist/frontend/src/stores/useChatStore.ts

298 lines
7.3 KiB
TypeScript

import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { io, type Socket } from 'socket.io-client'
export interface ChatMessage {
id: string
role: 'user' | 'assistant' | 'system'
content: string
timestamp: Date
metadata?: {
knowledge_used?: string[]
confidence_score?: number
work_order_id?: string
}
}
export interface ChatSession {
id: string
userId: string
workOrderId?: string
messages: ChatMessage[]
status: 'active' | 'ended'
createdAt: Date
}
export const useChatStore = defineStore('chat', () => {
// 状态
const socket = ref<Socket | null>(null)
const isConnected = ref(false)
const currentSession = ref<ChatSession | null>(null)
const messages = ref<ChatMessage[]>([])
const isTyping = ref(false)
const userId = ref('user_001')
const workOrderId = ref<string>('')
// 计算属性
const hasActiveSession = computed(() =>
currentSession.value && currentSession.value.status === 'active'
)
const messageCount = computed(() => messages.value.length)
// 动作
const connectWebSocket = () => {
return new Promise<void>((resolve, reject) => {
try {
socket.value = io('ws://localhost:8765', {
transports: ['websocket']
})
socket.value.on('connect', () => {
isConnected.value = true
resolve()
})
socket.value.on('disconnect', () => {
isConnected.value = false
})
socket.value.on('error', (error) => {
console.error('WebSocket error:', error)
reject(error)
})
socket.value.on('message_response', (data) => {
handleMessageResponse(data)
})
socket.value.on('typing_start', () => {
isTyping.value = true
})
socket.value.on('typing_end', () => {
isTyping.value = false
})
} catch (error) {
reject(error)
}
})
}
const disconnectWebSocket = () => {
if (socket.value) {
socket.value.disconnect()
socket.value = null
isConnected.value = false
}
}
const startChat = async () => {
try {
if (!socket.value) {
await connectWebSocket()
}
const response = await sendSocketMessage({
type: 'create_session',
user_id: userId.value,
work_order_id: workOrderId.value ? parseInt(workOrderId.value) : null
})
if (response.type === 'session_created') {
currentSession.value = {
id: response.session_id,
userId: userId.value,
workOrderId: workOrderId.value,
messages: [],
status: 'active',
createdAt: new Date()
}
addMessage('system', '对话已开始,请描述您的问题。')
return true
}
return false
} catch (error) {
console.error('启动对话失败:', error)
throw error
}
}
const endChat = async () => {
try {
if (currentSession.value) {
await sendSocketMessage({
type: 'end_session',
session_id: currentSession.value.id
})
currentSession.value.status = 'ended'
addMessage('system', '对话已结束。')
}
} catch (error) {
console.error('结束对话失败:', error)
}
}
const sendMessage = async (content: string) => {
if (!currentSession.value || !content.trim()) {
return
}
// 添加用户消息
addMessage('user', content)
try {
const response = await sendSocketMessage({
type: 'send_message',
session_id: currentSession.value.id,
message: content
})
if (response.type === 'message_response' && response.result.success) {
const result = response.result
// 添加助手回复
addMessage('assistant', result.content, {
knowledge_used: result.knowledge_used,
confidence_score: result.confidence_score,
work_order_id: result.work_order_id
})
// 更新工单ID
if (result.work_order_id) {
workOrderId.value = result.work_order_id.toString()
}
} else {
addMessage('assistant', '抱歉,我暂时无法处理您的问题。请稍后再试。')
}
} catch (error) {
console.error('发送消息失败:', error)
addMessage('assistant', '发送消息失败,请检查网络连接。')
}
}
const createWorkOrder = async (data: {
title: string
description: string
category: string
priority: string
}) => {
if (!currentSession.value) {
throw new Error('没有活跃的会话')
}
try {
const response = await sendSocketMessage({
type: 'create_work_order',
session_id: currentSession.value.id,
...data
})
if (response.type === 'work_order_created' && response.result.success) {
workOrderId.value = response.result.work_order_id.toString()
addMessage('system', `工单创建成功!工单号: ${response.result.order_id}`)
return response.result
} else {
throw new Error(response.result.error || '创建工单失败')
}
} catch (error) {
console.error('创建工单失败:', error)
throw error
}
}
const sendSocketMessage = (message: any): Promise<any> => {
return new Promise((resolve, reject) => {
if (!socket.value) {
reject(new Error('WebSocket未连接'))
return
}
const messageId = 'msg_' + Date.now()
message.messageId = messageId
const timeout = setTimeout(() => {
reject(new Error('请求超时'))
}, 10000)
const handleResponse = (data: any) => {
if (data.messageId === messageId) {
clearTimeout(timeout)
socket.value?.off('message_response', handleResponse)
resolve(data)
}
}
socket.value.on('message_response', handleResponse)
socket.value.emit('message', message)
})
}
const addMessage = (role: 'user' | 'assistant' | 'system', content: string, metadata?: any) => {
const message: ChatMessage = {
id: 'msg_' + Date.now() + '_' + Math.random(),
role,
content,
timestamp: new Date(),
metadata
}
messages.value.push(message)
if (currentSession.value) {
currentSession.value.messages.push(message)
}
}
const clearMessages = () => {
messages.value = []
if (currentSession.value) {
currentSession.value.messages = []
}
}
const setUserId = (id: string) => {
userId.value = id
}
const setWorkOrderId = (id: string) => {
workOrderId.value = id
}
const handleMessageResponse = (data: any) => {
// 处理WebSocket消息响应
console.log('收到消息响应:', data)
}
return {
// 状态
socket,
isConnected,
currentSession,
messages,
isTyping,
userId,
workOrderId,
// 计算属性
hasActiveSession,
messageCount,
// 动作
connectWebSocket,
disconnectWebSocket,
startChat,
endChat,
sendMessage,
createWorkOrder,
addMessage,
clearMessages,
setUserId,
setWorkOrderId
}
})