修复流式审核点击后无输出(静默失败)
原因:旧实现在返回 SseEmitter 之前同步做文档解析/渲染,一旦抛异常会被全局
异常处理器包成 JSON(HTTP 200 + {code,msg})返回;前端按 SSE 流读取,找不到
\n\n 分隔帧便静默结束——表现为“闪一下后无输出、也无报错”。
后端:
- analyzeStream 拆分 prepareSync(仅校验+读字节,必须在请求线程内)与
buildPrompt(解析/渲染/构建提示词)。buildPrompt 移入异步线程,任何异常都
转为 SSE error 事件返回,不再走 JSON 静默路径
- 线程启动即推送 start 事件,确认通道已打开
- 流式接口去掉 @Log(操作日志切面会尝试序列化 SseEmitter 返回值)
前端 add.vue:
- 校验响应 content-type:非 text/event-stream(鉴权失败/异常JSON/HTML)时读出
正文并弹出错误,避免静默
- 统计收到的事件数,全程零事件时提示“未收到流式数据,请检查后端能否访问AI服务”
- 处理 start 事件
注:服务端为 Undertow、未开 gzip 压缩,dev 代理默认透传,排除缓冲导致。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -115,6 +115,7 @@ export default {
|
||||
savedId: null,
|
||||
matchScore: null,
|
||||
riskLevel: null,
|
||||
eventCount: 0,
|
||||
|
||||
previewUrl: '',
|
||||
isPdf: false
|
||||
@@ -170,19 +171,27 @@ export default {
|
||||
try {
|
||||
const resp = await fetch(process.env.VUE_APP_BASE_API + '/oa/aiReview/analyzeStream', {
|
||||
method: 'POST',
|
||||
headers: { Authorization: 'Bearer ' + getToken() },
|
||||
headers: { Authorization: 'Bearer ' + getToken(), Accept: 'text/event-stream' },
|
||||
body: fd
|
||||
})
|
||||
if (!resp.ok || !resp.body) {
|
||||
let msg = '审核失败(' + resp.status + ')'
|
||||
try { const j = await resp.json(); if (j && j.msg) msg = j.msg } catch (e) {}
|
||||
// 非流式响应(鉴权失败 / 后端异常被包成 JSON / HTML)→ 读出来报错,避免静默
|
||||
const ct = resp.headers.get('content-type') || ''
|
||||
if (!resp.ok || !resp.body || ct.indexOf('text/event-stream') === -1) {
|
||||
let msg = '审核失败(HTTP ' + resp.status + ')'
|
||||
try {
|
||||
const txt = await resp.text()
|
||||
try { const j = JSON.parse(txt); if (j && (j.msg || j.message)) msg = j.msg || j.message }
|
||||
catch (e) { if (txt) msg = txt.slice(0, 200) }
|
||||
} catch (e) {}
|
||||
this.$modal.msgError(msg)
|
||||
this.eventCount = 0
|
||||
this.streaming = false
|
||||
return
|
||||
}
|
||||
const reader = resp.body.getReader()
|
||||
const decoder = new TextDecoder('utf-8')
|
||||
let buf = ''
|
||||
this.eventCount = 0
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
const { value, done } = await reader.read()
|
||||
@@ -195,6 +204,9 @@ export default {
|
||||
this.handleFrame(frame)
|
||||
}
|
||||
}
|
||||
if (this.eventCount === 0) {
|
||||
this.$modal.msgError('未收到任何流式数据,请检查后端是否可访问 AI 服务')
|
||||
}
|
||||
} catch (e) {
|
||||
this.$modal.msgError('连接中断:' + (e.message || e))
|
||||
} finally {
|
||||
@@ -210,7 +222,10 @@ export default {
|
||||
if (!data) return
|
||||
let ev
|
||||
try { ev = JSON.parse(data) } catch (e) { return }
|
||||
if (ev.type === 'reasoning') {
|
||||
this.eventCount++
|
||||
if (ev.type === 'start') {
|
||||
// 通道已打开,等待解析/生成
|
||||
} else if (ev.type === 'reasoning') {
|
||||
this.reasoning += ev.c
|
||||
this.scrollBottom()
|
||||
} else if (ev.type === 'content') {
|
||||
|
||||
Reference in New Issue
Block a user