第 05 章

对话翻译(SSE 流式)

会话管理、消息收发、SSE 流式翻译事件流与 AI 客服自动回复的完整事件时序。

5.1 SSE 机制总览

三条 SSE 通道
SSE /api/chat/sessions/{sessionId}/stream 流式消息翻译(发起翻译请求即在此连接上推送事件)
SSE /api/chat/sessions/{sessionId}/events 纯订阅通道:SessionSseHub 会话广播 + 20 秒心跳,多端同步消息
SSE /api/chat/sessions/{sessionId}/auto-reply AI 客服自动回复(机器人先说目标语言 → 再给源语言翻译)

SSE 响应为 text/event-stream,不经过 ApiResponseAdvice 统一包装。 连接由 chat.sse.executor 线程池异步驱动(8 核心 / 20 最大 / 500 队列,线程前缀 chat-sse-), 单连接超时 30 秒(SseEmitter(30_000L)),超时后推送 error 事件并关闭,客户端需自行重连。

streamMessage 事件时序(POST 消息后在 stream 连接上推送)
event: start
data: {"messageId": 1001}

event: delta                          // 多次,逐段推送译文
data: {"delta": "Hello"}              // 目标语言为 "en":8 字符/120ms;其他语言:16 字符/30ms

event: done
data: {"messageId": 1001, "translatedText": "Hello, how are you?"}

// 异常:event: error, data: "timeout"

⚠️ auto-reply 通道的 done 事件是纯文本(源语言完整回复),不是 JSON; 其流式过程为:先推 bot_start / bot_delta(AI 以目标语言回复,8 字符/120ms), 再推 delta(源语言翻译版本),最后 done = 源语言全文。

5.2 接口总览(前缀 /api/chat)

POST/api/chat/sessions创建会话
GET/api/chat/sessions?status=会话列表(按状态过滤)
GET/api/chat/sessions/{sessionId}会话详情
PUT/api/chat/sessions/{sessionId}更新会话
POST/api/chat/sessions/{sessionId}/end结束会话
POST/api/chat/messages发送消息(即时返回完整翻译)
GET/api/chat/sessions/{sessionId}/messages历史消息分页
SSE/api/chat/sessions/{sessionId}/stream | /events | /auto-reply三条 SSE 通道(见 5.1)

5.3 会话管理接口

POST

/api/chat/sessions

创建翻译会话。

请求体(CreateChatSessionRequest)
字段类型必填校验 / 说明
sessionNameString必填非空,≤ 100 字符
sourceLanguageString必填源语言代码
targetLanguageString必填目标语言代码
createdByString必填创建者标识
descriptionString可选会话描述
响应 data(ChatSessionResponse)
{
  "sessionId": 7,
  "sessionName": "客服咨询",
  "sourceLanguage": "zh", "targetLanguage": "en",
  "createdBy": "user-1",
  "createdAt": "2026-09-06T10:00:00",
  "status": "ACTIVE",
  "participantCount": 1,
  "messageCount": 0,
  "lastActivity": "2026-09-06T10:00:00",
  "description": "中英客服对话"
}
GET

/api/chat/sessions?status=ACTIVE

查询会话列表。status 为可选过滤参数,取值为 SessionStatusEnum 枚举名(不区分大小写,内部 valueOf(upper)):ACTIVE / INACTIVE / ENDED / ARCHIVED。

PUT

/api/chat/sessions/{sessionId}

更新会话名称、语言对或描述(UpdateChatSessionRequest:sessionName / sourceLanguage / targetLanguage / description)。

POST

/api/chat/sessions/{sessionId}/end

结束会话,状态置 ENDED。响应 data 为 null(仅以 success 判定)。

5.4 消息接口

POST

/api/chat/messages

发送消息并同步返回完整翻译结果(SSE 通道不传参,消息体在此提交)。默认参数下同时入库并触发客服链路。

请求体(SendChatMessageRequest)
字段类型必填默认值校验 / 说明
sessionIdLong必填—目标会话 ID
senderIdString必填—发送者标识
originalMessageString必填—消息原文,非空且 ≤ 5000 字符
sourceLanguageString必填—源语言
messageString可选—展示用消息内容
userIdLong可选—关联用户 ID
userNameString可选—展示名;响应中优先取昵称,其次用户名
receiverIdString可选—接收者标识
targetLanguageString可选en默认英文(默认参数源语言 zh / 目标 en)
messageTypeInteger可选00=普通消息
useTerminologyBoolean可选—透传至翻译链路(含 RAG 术语增强)
translationEngineString可选—指定引擎
响应 data(ChatMessageResponse)
{
  "messageId": 1001,
  "sessionId": 7,
  "userId": 1,
  "userName": "小明",
  "originalText": "你好,请问支持哪些语言?",
  "translatedText": "Hello, which languages are supported?",
  "sourceLanguage": "zh", "targetLanguage": "en",
  "sentAt": "2026-09-06T10:01:00",
  "messageType": 0,
  "readBy": [ ]
}

消息保存走 sendChatMessage 完整链路:术语库(useTerminology)+ RAG 增强均生效,与文本翻译共用计费。

GET

/api/chat/sessions/{sessionId}/messages

分页获取会话历史消息。

Query 参数
参数必填说明
page可选默认 0(0 起始)
size可选默认 20

响应 data 为分页对象,元素结构同 ChatMessageResponse。

5.5 SSE 客户端完整示例

const sessionId = 7;
const token = 'eyJhbGciOi...';

// 1. 建立 SSE 连接(EventSource 无法自定义 Header,浏览器端依赖 auth_token Cookie)
const es = new EventSource(`/api/chat/sessions/${sessionId}/stream`);

es.addEventListener('start', e => {
  console.log('翻译开始,消息 ID:', JSON.parse(e.data).messageId);
});

es.addEventListener('delta', e => {
  const { delta } = JSON.parse(e.data);
  outputEl.appendText(delta);              // 逐段追加渲染
});

es.addEventListener('done', e => {
  const { messageId, translatedText } = JSON.parse(e.data);
  console.log('完成:', translatedText);
});

es.addEventListener('error', () => {
  es.close();                              // 30 秒超时或服务端关闭
  reconnect();                             // 自行实现重连
});

// 2. 发送消息(fetch,与 SSE 连接解耦)
await fetch('/api/chat/messages', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${token}`
  },
  body: JSON.stringify({
    sessionId, senderId: 'user-1',
    originalMessage: '你好,请问支持哪些语言?',
    sourceLanguage: 'zh', targetLanguage: 'en',
    useTerminology: true
  })
});
# 观察 SSE 事件流(连接保持 30 秒后超时)
curl -N http://localhost:8090/api/chat/sessions/7/stream \
  -H "Authorization: Bearer $TOKEN"

# 另开终端发送消息,观察上方事件流输出
curl -X POST http://localhost:8090/api/chat/messages \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"sessionId":7,"senderId":"user-1","originalMessage":"你好","sourceLanguage":"zh","targetLanguage":"en"}'

# AI 客服自动回复通道
curl -N http://localhost:8090/api/chat/sessions/7/auto-reply \
  -H "Authorization: Bearer $TOKEN"