refactor(chat): wire sendMessageHttpActor for HTTP message sending and update response handling

This commit is contained in:
2026-06-12 11:45:46 +08:00
parent 38f060bbd8
commit a9938b8dae
3 changed files with 117 additions and 30 deletions
+84 -10
View File
@@ -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 sendMessageHttpActorHTTP 发送消息)
* - `loadingMore`invoke loadMoreHistory(翻历史)
* - `userSession`**parent**):非游客会话 —— **invoke WS**(持久)
* - `initializing`invoke loadMoreHistory(拉服务器端首屏)
* - `ready`:用户聊天 + 翻历史
* - `sending`invoke sendMessageHttpActorHTTP 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 }),