/**
* Agent 交互模块
*/
import { App } from './state.js';
import { escapeHtml } from './utils.js';
import { addChatMessage, showStatus } from './chat.js';
// ================================================================
// Agent 事件处理
// ================================================================
/**
* 处理 Agent SSE 事件
*
* @param {Object} msg - SSE 传来的 agent 事件对象
*/
export function handleAgentEvent(msg) {
switch (msg.type) {
case 'agent_state_change':
_handleAgentStateChange(msg);
break;
case 'agent_request_supplement':
_handleAgentRequestSupplement(msg);
break;
case 'agent_ready':
_handleAgentReady(msg);
break;
case 'agent_error':
_handleAgentError(msg);
break;
case 'agent_supplement_received':
_handleSupplementReceived(msg);
break;
case 'agent_force_submit':
_handleForceSubmit(msg);
break;
}
}
/**
* 状态变更通知 — 使用瞬态消息,状态变化时更新而非追加
*/
function _handleAgentStateChange(msg) {
const stateLabels = {
'extracting': '正在分析文件...',
'validating': '正在校验信息完整性...',
'awaiting_supplement': '等待补充材料',
'ready': '信息完整,可以提交',
'submitting': '正在提交...',
};
const label = stateLabels[msg.state] || msg.message || '处理中...';
if (msg.state === 'ready') {
showStatus(label, 'done');
} else {
showStatus(label);
}
}
/**
* 请求补充材料 — 仅更新瞬态状态,持久提示消息由 done 事件分支负责
*/
export function _handleAgentRequestSupplement(msg) {
const suggestion = msg.suggestion || '请补充上传相关材料';
showStatus(suggestion);
}
/**
* 信息完整 — 已由 process.js 的 done 处理覆盖,此处不再重复显示
*/
function _handleAgentReady(msg) {
// agent_ready 事件的信息展示统一由 process.js done 分支处理
}
/**
* 自动触发财务系统提交(由 done 事件调用)
*/
export async function handleAutoSubmit() {
if (!App.sessionId) {
addChatMessage('请先上传文件并处理', 'error');
return;
}
if (App.agentEventSource) {
App.agentEventSource.close();
App.agentEventSource = null;
}
App.isProcessing = true;
App.processState = 'submitting';
try {
showStatus('正在自动提交到财务系统...');
const response = await fetch(`/api/submit-financial/${App.sessionId}`, {
method: 'POST',
});
const result = await response.json();
if (result.status === 'started') {
const es = new EventSource(`/api/logs/${App.sessionId}`);
es.addEventListener('message', e => {
try {
const msg = JSON.parse(e.data);
if (msg.type === 'done') {
es.close();
App.isProcessing = false;
App.processState = 'done';
if (msg.result.submit_ok) {
addChatMessage('提交完成!', 'done');
} else {
addChatMessage(`提交失败:${msg.result.submit_error || '未知错误'}`, 'error');
}
}
} catch (err) {
// 普通日志行
}
});
es.onerror = () => {
es.close();
App.isProcessing = false;
App.processState = 'done';
addChatMessage('连接中断', 'error');
};
} else if (result.error) {
App.isProcessing = false;
App.processState = 'done';
addChatMessage(`提交失败:${result.error}`, 'error');
}
} catch (e) {
App.isProcessing = false;
App.processState = 'done';
addChatMessage(`请求失败:${e.message}`, 'error');
}
}
/**
* Agent 错误
*/
function _handleAgentError(msg) {
addChatMessage(msg.message || 'Agent 处理出错', 'error');
App.isProcessing = false;
App.processState = 'done';
}
/**
* 收到补充文件
*/
function _handleSupplementReceived(msg) {
const fileNames = (msg.files || []).join('、');
addChatMessage(`已收到补充文件:${fileNames}`, 'done');
}
/**
* 强制提交
*/
function _handleForceSubmit(msg) {
showStatus('已跳过校验,开始提交到财务系统');
}
// ================================================================
// Agent 请求面板
// ================================================================
/**
* 显示 Agent 请求补充材料的 UI
*/
export function showAgentRequest(data) {
const existing = document.getElementById('agent-request-panel');
if (existing) existing.remove();
const chatMessages = document.getElementById('chat-messages');
if (!chatMessages) return;
const panel = document.createElement('div');
panel.id = 'agent-request-panel';
panel.className = 'agent-request-panel';
let materialsHtml = '';
if (data.missing_materials && data.missing_materials.length > 0) {
materialsHtml = `
需要补充:
${data.missing_materials.map(m => `${escapeHtml(m)}`).join(' ')}
`;
}
panel.innerHTML = `
${materialsHtml}
`;
chatMessages.appendChild(panel);
chatMessages.scrollTop = chatMessages.scrollHeight;
}
/**
* 移除 Agent 请求面板
*/
export function hideAgentRequest() {
const panel = document.getElementById('agent-request-panel');
if (panel) panel.remove();
}
// ================================================================
// 用户文字补充
// ================================================================
/**
* 处理用户通过输入框补充的文字信息
*
* @param {string} text - 用户输入的文字
*/
export async function handleUserSupplement(text) {
if (!App.sessionId) return;
try {
showStatus('正在解析补充信息...');
const response = await fetch(`/api/agent/user-supplement/${App.sessionId}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: text }),
});
const result = await response.json();
if (result.status === 'started') {
showStatus('补充信息已收到,正在重新分析...');
const es = new EventSource(`/api/logs/${App.sessionId}`);
es.addEventListener('message', e => {
try {
const msg = JSON.parse(e.data);
if (msg.type && msg.type.startsWith('agent_')) {
handleAgentEvent(msg);
return;
}
if (msg.type === 'done') {
es.close();
App.isProcessing = false;
if (msg.result.waiting_for_supplement) {
App.processState = 'awaiting_supplement';
_handleAgentRequestSupplement(msg.result);
addChatMessage('您可通过上传区补充文件,或直接输入文字说明补充信息,或输入"直接提交"跳过校验', 'system');
} else {
App.processState = 'done';
if (msg.result.ok) {
if (msg.result.submit_ok !== false) {
addChatMessage('信息完整,已自动提交到财务系统', 'done');
} else {
addChatMessage(`校验通过但提交失败:${msg.result.submit_error || '未知错误'}`, 'error');
}
} else {
addChatMessage(`分析失败:${msg.result.error || '未知错误'}`, 'error');
}
}
}
} catch (err) {
// 普通日志
}
});
es.onerror = () => {
es.close();
App.isProcessing = false;
App.processState = 'done';
addChatMessage('连接中断', 'error');
};
} else if (result.error) {
addChatMessage(`处理失败:${result.error}`, 'error');
}
} catch (e) {
addChatMessage(`请求失败:${e.message}`, 'error');
}
}
// ================================================================
// 用户输入处理
// ================================================================
/**
* 处理强制提交
*/
export async function handleForceSubmit() {
if (!App.sessionId) {
addChatMessage('请先上传文件并处理', 'error');
return;
}
if (App.agentEventSource) {
App.agentEventSource.close();
App.agentEventSource = null;
}
App.forceSubmitting = true;
try {
showStatus('正在跳过校验并提交...');
hideAgentRequest();
const response = await fetch(`/api/agent/force-submit/${App.sessionId}`, {
method: 'POST',
});
const result = await response.json();
if (result.status === 'started') {
showStatus('已跳过校验,开始提交到财务系统');
App.isProcessing = true;
App.processState = 'submitting';
const es = new EventSource(`/api/logs/${App.sessionId}`);
es.addEventListener('message', e => {
try {
const msg = JSON.parse(e.data);
if (msg.type === 'done') {
es.close();
App.isProcessing = false;
App.processState = 'done';
App.forceSubmitting = false;
App.lastProcessedFileCount = App.allFiles.length;
if (msg.result.ok) {
addChatMessage('提交完成!', 'done');
} else {
addChatMessage(`提交失败:${msg.result.submit_error || '未知错误'}`, 'error');
}
}
} catch (err) {
// 普通日志行
}
});
es.onerror = () => {
es.close();
App.isProcessing = false;
App.processState = 'done';
App.forceSubmitting = false;
App.lastProcessedFileCount = App.allFiles.length;
addChatMessage('连接中断', 'error');
};
}
} catch (e) {
App.forceSubmitting = false;
addChatMessage(`请求失败:${e.message}`, 'error');
}
}
// ================================================================
// 补充文件处理
// ================================================================
/**
* 处理补充文件上传完成后的重新分析
*
* @param {Array} filenames - 新上传的文件名列表
*/
export async function handleSupplementUpload(filenames) {
if (!App.sessionId) return;
try {
showStatus('收到补充文件,正在重新分析...');
hideAgentRequest();
const response = await fetch(`/api/agent/supplement/${App.sessionId}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ files: filenames }),
});
const result = await response.json();
if (result.status === 'started') {
const es = new EventSource(`/api/logs/${App.sessionId}`);
es.addEventListener('message', e => {
try {
const msg = JSON.parse(e.data);
if (msg.type && msg.type.startsWith('agent_')) {
handleAgentEvent(msg);
return;
}
if (msg.type === 'done') {
es.close();
App.isProcessing = false;
if (msg.result.waiting_for_supplement) {
App.processState = 'awaiting_supplement';
showStatus('信息不完整,请补充上传相关材料');
addChatMessage('您可通过上传区补充文件,或直接输入文字说明补充信息,或输入"直接提交"跳过校验', 'system');
} else {
App.processState = 'done';
if (msg.result.ok) {
// 后端已自动提交,根据 submit_ok 显示结果
if (msg.result.submit_ok !== false) {
addChatMessage('信息完整,已自动提交到财务系统', 'done');
} else {
addChatMessage(`校验通过但提交失败:${msg.result.submit_error || '未知错误'}`, 'error');
}
} else {
addChatMessage(`分析失败:${msg.result.error || '未知错误'}`, 'error');
}
}
}
} catch (err) {
// 普通日志
}
});
es.onerror = () => {
es.close();
App.isProcessing = false;
App.processState = 'done';
addChatMessage('连接中断', 'error');
};
}
} catch (e) {
addChatMessage(`请求失败:${e.message}`, 'error');
}
}