2026-07-07 21:52:11 +08:00
|
|
|
|
// =============================================================================
|
|
|
|
|
|
// 企微IT智能服务台 — 阶段5 自动化闭环 状态管理(Pinia Store,坐席端)
|
|
|
|
|
|
// =============================================================================
|
|
|
|
|
|
// 说明:管理自动化会话列表、当前会话详情,以及专用 WebSocket 实时推送。
|
|
|
|
|
|
// =============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
import { defineStore } from 'pinia'
|
|
|
|
|
|
import { ref, computed } from 'vue'
|
|
|
|
|
|
import type {
|
|
|
|
|
|
AutomationSession,
|
|
|
|
|
|
CreateSessionPayload,
|
|
|
|
|
|
ApprovalPayload,
|
|
|
|
|
|
TakeoverPayload,
|
2026-07-11 23:13:10 +08:00
|
|
|
|
InformationItem,
|
|
|
|
|
|
PausedSession,
|
2026-07-07 21:52:11 +08:00
|
|
|
|
} from '@/api/automation'
|
|
|
|
|
|
import {
|
|
|
|
|
|
createAutomationSession,
|
|
|
|
|
|
listAutomationSessions,
|
|
|
|
|
|
getAutomationSession,
|
|
|
|
|
|
approveAutomationSession,
|
|
|
|
|
|
takeoverAutomationSession,
|
2026-07-11 23:13:10 +08:00
|
|
|
|
listPausedSessions,
|
|
|
|
|
|
agentResumeSession,
|
|
|
|
|
|
agentCloseSession,
|
|
|
|
|
|
getInfoItems,
|
2026-07-07 21:52:11 +08:00
|
|
|
|
} from '@/api/automation'
|
|
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
2026-07-07 22:56:49 +08:00
|
|
|
|
// WebSocket 辅助(C8:移除 portal_token)
|
2026-07-07 21:52:11 +08:00
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
|
function getAgentToken(): string {
|
2026-07-07 22:56:49 +08:00
|
|
|
|
return localStorage.getItem('agent_token') || ''
|
2026-07-07 21:52:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function buildWsUrl(sessionId: string): string {
|
|
|
|
|
|
const token = getAgentToken()
|
|
|
|
|
|
const isDev = import.meta.env.DEV
|
|
|
|
|
|
const proto = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
|
|
|
|
|
|
const host = isDev ? 'localhost:8000' : window.location.host
|
|
|
|
|
|
return `${proto}//${host}/ws/automation/${sessionId}?token=${encodeURIComponent(token)}`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export const useAutomationStore = defineStore('automation', () => {
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// 状态
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
const sessions = ref<AutomationSession[]>([])
|
|
|
|
|
|
const currentSession = ref<AutomationSession | null>(null)
|
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
|
const ws = ref<WebSocket | null>(null)
|
|
|
|
|
|
const wsSessionId = ref<string | null>(null)
|
|
|
|
|
|
const wsConnected = ref(false)
|
2026-07-11 23:13:10 +08:00
|
|
|
|
/** 暂停会话列表(复杂场景重构) */
|
|
|
|
|
|
const pausedSessions = ref<PausedSession[]>([])
|
|
|
|
|
|
/** 当前会话信息项列表(复杂场景重构) */
|
|
|
|
|
|
const infoItems = ref<InformationItem[]>([])
|
2026-07-07 21:52:11 +08:00
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// 计算属性
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
const pendingSessions = computed(() =>
|
|
|
|
|
|
sessions.value.filter((s) => ['created', 'running', 'paused'].includes(s.status)),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// 数据加载
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
async function fetchSessions(): Promise<void> {
|
|
|
|
|
|
loading.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
sessions.value = await listAutomationSessions()
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
loading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function fetchSession(sessionId: string): Promise<void> {
|
|
|
|
|
|
loading.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
currentSession.value = await getAutomationSession(sessionId)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
loading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function createSession(payload: CreateSessionPayload): Promise<AutomationSession> {
|
|
|
|
|
|
const session = await createAutomationSession(payload)
|
|
|
|
|
|
currentSession.value = session
|
|
|
|
|
|
await fetchSessions()
|
|
|
|
|
|
return session
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function approve(sessionId: string, decision: 'approve' | 'reject', note?: string): Promise<void> {
|
|
|
|
|
|
const payload: ApprovalPayload = { decision, note }
|
|
|
|
|
|
currentSession.value = await approveAutomationSession(sessionId, payload)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function takeover(sessionId: string, agentId: string, note?: string): Promise<void> {
|
|
|
|
|
|
const payload: TakeoverPayload = { agent_id: agentId, note }
|
|
|
|
|
|
currentSession.value = await takeoverAutomationSession(sessionId, payload)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-11 23:13:10 +08:00
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// 复杂场景重构:暂停会话管理 / 信息项
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
async function fetchPausedSessions(employeeId: string): Promise<void> {
|
|
|
|
|
|
pausedSessions.value = await listPausedSessions(employeeId)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function agentResume(sessionId: string, note?: string): Promise<void> {
|
|
|
|
|
|
currentSession.value = await agentResumeSession(sessionId, { note })
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function agentClose(sessionId: string, note?: string): Promise<void> {
|
|
|
|
|
|
currentSession.value = await agentCloseSession(sessionId, { note })
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function fetchInfoItems(sessionId: string): Promise<void> {
|
|
|
|
|
|
infoItems.value = await getInfoItems(sessionId)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-07 21:52:11 +08:00
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// WebSocket
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
function connectWs(sessionId: string): void {
|
|
|
|
|
|
disconnectWs()
|
|
|
|
|
|
const url = buildWsUrl(sessionId)
|
|
|
|
|
|
const token = getAgentToken()
|
|
|
|
|
|
const socket = new WebSocket(url, [`bearer.${token}`])
|
|
|
|
|
|
ws.value = socket
|
|
|
|
|
|
wsSessionId.value = sessionId
|
|
|
|
|
|
|
|
|
|
|
|
socket.onopen = () => {
|
|
|
|
|
|
wsConnected.value = true
|
|
|
|
|
|
}
|
|
|
|
|
|
socket.onmessage = (event: MessageEvent) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const msg = JSON.parse(event.data)
|
|
|
|
|
|
handleWsMessage(msg)
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error('[automation WS] 消息解析失败', e)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
socket.onclose = () => {
|
|
|
|
|
|
wsConnected.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
socket.onerror = () => {
|
|
|
|
|
|
wsConnected.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function disconnectWs(): void {
|
|
|
|
|
|
if (ws.value) {
|
|
|
|
|
|
ws.value.close()
|
|
|
|
|
|
ws.value = null
|
|
|
|
|
|
}
|
|
|
|
|
|
wsConnected.value = false
|
|
|
|
|
|
wsSessionId.value = null
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleWsMessage(msg: { type: string; session_id?: string; data?: any }): void {
|
|
|
|
|
|
// 仅处理当前会话的事件
|
|
|
|
|
|
if (msg.session_id && wsSessionId.value && msg.session_id !== wsSessionId.value) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
switch (msg.type) {
|
|
|
|
|
|
case 'automation.progress':
|
|
|
|
|
|
case 'automation.action_required':
|
|
|
|
|
|
case 'automation.resolved':
|
|
|
|
|
|
case 'automation.takeover':
|
|
|
|
|
|
case 'automation.error':
|
|
|
|
|
|
// 有事件即刷新详情(保持简单可靠)
|
|
|
|
|
|
if (wsSessionId.value) {
|
|
|
|
|
|
fetchSession(wsSessionId.value)
|
|
|
|
|
|
}
|
|
|
|
|
|
break
|
2026-07-11 23:13:10 +08:00
|
|
|
|
// --- 复杂场景重构:新增 WS 事件 ---
|
|
|
|
|
|
case 'automation.paused':
|
|
|
|
|
|
// 会话暂停:刷新详情
|
|
|
|
|
|
if (wsSessionId.value) {
|
|
|
|
|
|
fetchSession(wsSessionId.value)
|
|
|
|
|
|
}
|
|
|
|
|
|
break
|
|
|
|
|
|
case 'automation.resumed':
|
|
|
|
|
|
// 会话恢复:刷新详情和信息项
|
|
|
|
|
|
if (wsSessionId.value) {
|
|
|
|
|
|
fetchSession(wsSessionId.value)
|
|
|
|
|
|
fetchInfoItems(wsSessionId.value)
|
|
|
|
|
|
}
|
|
|
|
|
|
break
|
|
|
|
|
|
case 'automation.info_corrected':
|
|
|
|
|
|
// 信息更正:刷新信息项
|
|
|
|
|
|
if (wsSessionId.value) {
|
|
|
|
|
|
fetchInfoItems(wsSessionId.value)
|
|
|
|
|
|
}
|
|
|
|
|
|
break
|
|
|
|
|
|
case 'automation.info_supplemented':
|
|
|
|
|
|
// 信息补充:刷新信息项
|
|
|
|
|
|
if (wsSessionId.value) {
|
|
|
|
|
|
fetchInfoItems(wsSessionId.value)
|
|
|
|
|
|
}
|
|
|
|
|
|
break
|
|
|
|
|
|
case 'automation.timeout_closed':
|
|
|
|
|
|
// 超时关闭:刷新详情
|
|
|
|
|
|
if (wsSessionId.value) {
|
|
|
|
|
|
fetchSession(wsSessionId.value)
|
|
|
|
|
|
}
|
|
|
|
|
|
break
|
2026-07-07 21:52:11 +08:00
|
|
|
|
default:
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
sessions,
|
|
|
|
|
|
currentSession,
|
|
|
|
|
|
loading,
|
|
|
|
|
|
wsConnected,
|
|
|
|
|
|
pendingSessions,
|
2026-07-11 23:13:10 +08:00
|
|
|
|
pausedSessions,
|
|
|
|
|
|
infoItems,
|
2026-07-07 21:52:11 +08:00
|
|
|
|
fetchSessions,
|
|
|
|
|
|
fetchSession,
|
|
|
|
|
|
createSession,
|
|
|
|
|
|
approve,
|
|
|
|
|
|
takeover,
|
2026-07-11 23:13:10 +08:00
|
|
|
|
fetchPausedSessions,
|
|
|
|
|
|
agentResume,
|
|
|
|
|
|
agentClose,
|
|
|
|
|
|
fetchInfoItems,
|
2026-07-07 21:52:11 +08:00
|
|
|
|
connectWs,
|
|
|
|
|
|
disconnectWs,
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|