refactor(chat): wire sendMessageHttpActor for HTTP message sending and update response handling
This commit is contained in:
@@ -1,13 +1,6 @@
|
||||
/**
|
||||
* Chat 状态机(XState v5)
|
||||
*
|
||||
* 原始 Dart: lib/ui/chat/bloc/chat bloc + chat state + chat event
|
||||
*
|
||||
* 本轮迁移范围:
|
||||
* ✅ HTTP 流程(init / send message / load more history)
|
||||
* ✅ WebSocket 长生命周期(**由** chat 机器内 fromCallback actor 管)
|
||||
* ✅ 上下文数据(messages / quota / 标志位)
|
||||
*
|
||||
* 设计要点:
|
||||
* - 使用 XState v5 `setup({...}).createMachine({...})` 声明式 API
|
||||
* - HTTP 操作用 `fromPromise` actor(一次性 Promise)
|
||||
@@ -27,15 +20,17 @@
|
||||
* - `guestSession`(**parent**):游客会话 —— **不** invoke WS
|
||||
* - `initializing`:invoke chatInit(拉本地 + 初始化配额)
|
||||
* - `ready`:用户聊天 + 翻历史
|
||||
* - `sending`:invoke sendMessageHttpActor(HTTP 发送消息)
|
||||
* - `loadingMore`:invoke loadMoreHistory(翻历史)
|
||||
* - `userSession`(**parent**):非游客会话 —— **invoke WS**(持久)
|
||||
* - `initializing`:invoke loadMoreHistory(拉服务器端首屏)
|
||||
* - `ready`:用户聊天 + 翻历史
|
||||
* - `sending`:invoke sendMessageHttpActor(HTTP fallback —— WS 真发**未** wire)
|
||||
* - `loadingMore`:invoke loadMoreHistory(翻历史)
|
||||
* WS 在 `userSession` parent 状态 invoke —— 跨 initializing/ready/loadingMore 持续
|
||||
* WS 在 `userSession` parent 状态 invoke —— 跨 initializing/ready/sending/loadingMore 持续
|
||||
*
|
||||
* **消息发送路径**(**业务事实**):
|
||||
* - `wsConnected: true`(**仅** userSession 期间)→ **走 WS**,**不**消耗次数
|
||||
* - `wsConnected: true`(**仅** userSession 期间)→ **走 WS**,**不**消耗次数(**仅**配额规则)
|
||||
* - `wsConnected: false` → **走 HTTP**,消耗次数(**仅**游客有意义 —— 非游客 0-1=max(0,-1)=0 天然 no-op)
|
||||
* - `ChatWebSocketConnected` 事件 → `wsConnected = true`
|
||||
* - `userSession.exit` action → `wsConnected = false`(WS actor cleanup 时**也**会跑 exit)
|
||||
@@ -279,6 +274,7 @@ export const chatMachine = setup({
|
||||
ChatSendMessage: {
|
||||
actions: ["logSendMessageReceived", "appendUserMessage"],
|
||||
guard: ({ event }) => event.content.trim().length > 0,
|
||||
target: "sending",
|
||||
},
|
||||
ChatSendImage: {
|
||||
actions: ["logSendImageReceived", "appendUserImage"],
|
||||
@@ -302,6 +298,44 @@ export const chatMachine = setup({
|
||||
ChatQuotaExceeded: { actions: "incrementQuotaExceeded" },
|
||||
},
|
||||
},
|
||||
sending: {
|
||||
entry: ({ event }) =>
|
||||
console.log("[chat-machine] → guestSession.sending", {
|
||||
contentLength: event.type === "ChatSendMessage" ? event.content.length : 0,
|
||||
contentPreview: event.type === "ChatSendMessage" ? event.content.slice(0, 50) : "",
|
||||
}),
|
||||
invoke: {
|
||||
src: "sendMessageHttp",
|
||||
input: ({ event }) => ({
|
||||
content: event.type === "ChatSendMessage" ? event.content : "",
|
||||
}),
|
||||
onDone: {
|
||||
target: "ready",
|
||||
actions: [
|
||||
assign(({ context, event }) => ({
|
||||
messages: [...context.messages, event.output.reply],
|
||||
isReplyingAI: false,
|
||||
})),
|
||||
({ context, event }) =>
|
||||
console.log("[chat-machine] guestSession.sending.onDone", {
|
||||
newReplyContentLength: event.output.reply.content.length,
|
||||
totalMessagesCount: context.messages.length + 1,
|
||||
isReplyingAI: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
onError: {
|
||||
target: "ready",
|
||||
actions: [
|
||||
assign({ isReplyingAI: false }),
|
||||
({ event }) =>
|
||||
console.error("[chat-machine] guestSession.sending.onError", {
|
||||
error: event.error instanceof Error ? event.error.message : String(event.error),
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
loadingMore: {
|
||||
entry: ({ context }) =>
|
||||
console.log("[chat-machine] → guestSession.loadingMore", { offset: context.historyOffset }),
|
||||
@@ -357,7 +391,7 @@ export const chatMachine = setup({
|
||||
onDone: {
|
||||
target: "ready",
|
||||
actions: [
|
||||
assign(({ event }) => ({
|
||||
assign(({ context, event }) => ({
|
||||
messages: event.output.messages,
|
||||
isLoadingMore: false,
|
||||
hasMore: event.output.hasMore,
|
||||
@@ -388,6 +422,7 @@ export const chatMachine = setup({
|
||||
ChatSendMessage: {
|
||||
actions: ["logSendMessageReceived", "appendUserMessage"],
|
||||
guard: ({ event }) => event.content.trim().length > 0,
|
||||
target: "sending",
|
||||
},
|
||||
ChatSendImage: {
|
||||
actions: ["logSendImageReceived", "appendUserImage"],
|
||||
@@ -411,6 +446,45 @@ export const chatMachine = setup({
|
||||
ChatQuotaExceeded: { actions: "incrementQuotaExceeded" },
|
||||
},
|
||||
},
|
||||
sending: {
|
||||
entry: ({ event }) =>
|
||||
console.log("[chat-machine] → userSession.sending", {
|
||||
contentLength: event.type === "ChatSendMessage" ? event.content.length : 0,
|
||||
contentPreview: event.type === "ChatSendMessage" ? event.content.slice(0, 50) : "",
|
||||
wsConnected: true,
|
||||
}),
|
||||
invoke: {
|
||||
src: "sendMessageHttp",
|
||||
input: ({ event }) => ({
|
||||
content: event.type === "ChatSendMessage" ? event.content : "",
|
||||
}),
|
||||
onDone: {
|
||||
target: "ready",
|
||||
actions: [
|
||||
assign(({ context, event }) => ({
|
||||
messages: [...context.messages, event.output.reply],
|
||||
isReplyingAI: false,
|
||||
})),
|
||||
({ context, event }) =>
|
||||
console.log("[chat-machine] userSession.sending.onDone", {
|
||||
newReplyContentLength: event.output.reply.content.length,
|
||||
totalMessagesCount: context.messages.length + 1,
|
||||
isReplyingAI: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
onError: {
|
||||
target: "ready",
|
||||
actions: [
|
||||
assign({ isReplyingAI: false }),
|
||||
({ event }) =>
|
||||
console.error("[chat-machine] userSession.sending.onError", {
|
||||
error: event.error instanceof Error ? event.error.message : String(event.error),
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
loadingMore: {
|
||||
entry: ({ context }) =>
|
||||
console.log("[chat-machine] → userSession.loadingMore", { offset: context.historyOffset }),
|
||||
|
||||
Reference in New Issue
Block a user