提交 00694c49 authored 作者: 朱亚刚's avatar 朱亚刚

完成pdf写报接口接入

上级 3c267bc1
...@@ -294,17 +294,84 @@ const getStreamChat = async (search, inputValue) => { ...@@ -294,17 +294,84 @@ const getStreamChat = async (search, inputValue) => {
ElMessage.error('文件解析失败,请重新选择'); ElMessage.error('文件解析失败,请重新选择');
return; return;
} }
callSseApi(rawFile) callSseWithPdf(rawFile)
} else { } else {
abortController.value = new AbortController();
const params = { const params = {
query: writtingTitle.value, // "输出一篇报文" query: writtingTitle.value, // "输出一篇报文"
desc: descText.value, desc: descText.value,
topic: curTempTitle.value // 政令、智库、法案、清单 topic: curTempTitle.value // 政令、智库、法案、清单
}; };
callSseWithAi(params)
}
};
const callSseWithPdf = async (selectedFile) => {
abortController.value = new AbortController();
try {
// 构造FormData(和后端字段名保持一致)
const formData = new FormData();
formData.append('pdf', selectedFile);
// 调用fetchEventSource(核心:支持POST+FormData+SSE)
await fetchEventSource('/pdfSse/api/v1/order/pdf/extract/report/sse', {
method: 'POST', // 关键:设置POST方法
body: formData, // 关键:传递PDF文件的FormData
signal: abortController.value.signal, // 中断信号
headers: {
// 禁用默认的SSE协议头(避免和文件上传冲突)
'Accept': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
},
// 禁用自动重连(可选,根据后端配置)
retry: 0,
// 核心:原生onmessage回调(无需手动分割/解析)
async onopen(res) {
console.log("流式回答开始", res);
isGenerating.value = true;
isShowProcess.value = true;
},
async onmessage(res) {
const { data, event } = res
const jsonData = JSON.parse(data)
switch (event) {
case "progress":
processContent.value += `${getFormattedTime()}:${jsonData.message}\r\n`;
updateProcess(processContent.value, scrollProcessContainer.value);
break;
case "result":
callSseWithAi({
query: writtingTitle.value, // "输出一篇报文"
desc: descText.value,
topic: "政令",
result: data // 政令、智库、法案、清单
})
default:
break;
}
},
onerror(error) {
ElMessage({
message: "写报生成报错!",
type: "warning"
});
abortController.value.abort();
abortController.value = new AbortController();
throw new Error(error);
}
});
} catch (error) {
if (error.name !== 'AbortError') {
ElMessage.error(`请求失败:${error.message}`);
isLoading.value = false;
}
}
};
const callSseWithAi = async (params) => {
abortController.value = new AbortController();
fetchEventSource("/sseWrite/api/v1/workflow/invoke", { fetchEventSource("/sseWrite/api/v1/workflow/invoke", {
method: "POST", method: "POST",
headers: { headers: {
...@@ -367,204 +434,16 @@ const getStreamChat = async (search, inputValue) => { ...@@ -367,204 +434,16 @@ const getStreamChat = async (search, inputValue) => {
abortController.value = new AbortController(); abortController.value = new AbortController();
throw new Error(error); throw new Error(error);
}); });
} }
};
const callSseApi = async (selectedFile) => {
abortController.value = new AbortController();
try {
const formData = new FormData();
formData.append('pdf', selectedFile);
const response = await fetch('/pdfSse/api/v1/order/pdf/extract/report/sse', {
method: 'POST',
body: formData,
signal: abortController.value.signal, // 绑定中断信号
});
if (!response.ok) {
throw new Error(`请求失败:${response.status} ${response.statusText}`);
}
console.log("流式回答开始", res);
isGenerating.value = true;
isShowProcess.value = true;
// 读取流式响应
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = ''; // 消息缓冲区
// 定义SSE消息回调(和原生onmessage用法一致)
const onmessage = (res) => {
const { type, data } = res
if (type === "progress") {
processContent.value += data.message;
updateProcess(processContent.value, scrollProcessContainer.value);
}
};
// 错误回调
const onerror = (error) => {
};
// 连接关闭回调
const onclose = () => {
};
// 循环读取并解析SSE消息(核心修复换行符兼容)
while (true) {
try {
const { done, value } = await reader.read();
// 连接正常关闭
if (done) {
onclose();
break;
}
// 1. 解码二进制流为文本,追加到缓冲区
buffer += decoder.decode(value, { stream: true });
// 2. 分割完整消息(兼容 \r\n\r\n 和 \n\n 换行符)
const fullMessages = buffer.split(/(\r\n|\n){2}/);
// 过滤空字符串,只保留有效消息
const validMessages = fullMessages.filter(msg => msg.trim() !== '');
// 3. 最后一条可能是不完整消息,放回缓冲区
let remainingBuffer = '';
if (validMessages.length > 0) {
remainingBuffer = validMessages.pop() || '';
}
// 4. 逐条解析有效消息
validMessages.forEach(fullMsg => {
const sseMsg = parseSSEMessage(fullMsg);
// 过滤心跳/注释消息(以:开头的消息)
if (sseMsg.isComment) return;
// 触发onmessage回调
if (sseMsg.data) {
debugger
onmessage({
type: sseMsg.event || 'message',
data: JSON.parse(sseMsg.data), // 解析为JSON对象
timeStamp: Date.now()
});
}
});
// 重置缓冲区(只保留不完整的消息)
buffer = remainingBuffer;
} catch (error) {
// 排除主动中断的情况
if (error.name !== 'AbortError') {
onerror(error);
}
break;
}
}
} catch (error) {
// 捕获请求级别的错误
if (error.name !== 'AbortError') {
ElMessage.error(`请求异常:${error.message}`);
isLoading.value = false;
}
}
};
// 工具函数:严格按SSE协议解析单条消息
const parseSSEMessage = (rawMsg) => {
// 兼容 \r\n 和 \n 分割行
const lines = rawMsg.split(/\r\n|\n/);
const result = {
event: 'progress', // 默认事件类型
data: '', // 消息数据
isComment: false // 是否是注释/心跳消息
};
lines.forEach(line => {
line = line.trimEnd(); // 去掉行尾空白符
// 注释行(以:开头):心跳消息,标记为注释
if (line.startsWith(':')) {
result.isComment = true;
return;
}
// 解析event行(event: xxx)
if (line.startsWith('event:')) {
result.event = line.slice(6).trim();
return;
}
// 解析data行(data: xxx),支持多行data拼接
if (line.startsWith('data:')) {
result.data += line.slice(5).trim();
return;
}
// 空行忽略 const getFormattedTime = () => {
if (line === '') return; const now = new Date();
}); // 补零函数:确保单个数字补为两位(如 1 → 01,9 → 09)
const pad = n => n.toString().padStart(2, '0');
return result; return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
}; };
const ele = () => {
return new Promise((resolve, reject) => {
// 转换FormData为Blob(适配SSE的POST请求体)
const formDataBlob = new Blob([
// 模拟multipart/form-data的边界符(简化版,实际可复用浏览器自动生成的)
...Array.from(formData.entries()).map(([key, value]) => {
return `--boundary\r\nContent-Disposition: form-data; name="${key}"${value instanceof File ? `; filename="${value.name}"\r\nContent-Type: ${value.type}` : ''}\r\n\r\n${value instanceof File ? value : value}\r\n`;
}),
'--boundary--\r\n'
]);
fetchEventSource('/pdfSse/api/v1/order/pdf/extract/report/sse', {
method: 'POST',
headers: {
// 关键:设置multipart/form-data头,包含边界符
'Content-Type': `multipart/form-data; boundary=boundary`,
},
// 注意:fetchEventSource的body仅支持字符串/ArrayBuffer/Blob,这里传FormData转换后的Blob
body: formDataBlob,
signal: abortController.value.signal,
openWhenHidden: true,
// SSE连接开启
async onopen(res) {
console.log("流式回答开始", res);
isGenerating.value = true;
isShowProcess.value = true;
},
async onmessage(res) {
debugger
const { event, data } = res
let jsonData = JSON.parse(data);
if (event === "progress") {
processContent.value += jsonData.message;
updateProcess(processContent.value, scrollProcessContainer.value);
}
},
onerror(error) {
ElMessage({
message: "写报生成报错!",
type: "warning"
});
abortController.value.abort();
abortController.value = new AbortController();
throw new Error(error);
}
}).catch((error) => {
reject(error);
isProcessing.value = false;
});
});
}
const writtingTitle = ref(""); const writtingTitle = ref("");
const descText = ref(""); const descText = ref("");
const tabList = ref([ const tabList = ref([
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论