diff --git a/src/stores/chat/chat-machine.ts b/src/stores/chat/chat-machine.ts index 8c7d6828..5a568844 100644 --- a/src/stores/chat/chat-machine.ts +++ b/src/stores/chat/chat-machine.ts @@ -11,21 +11,7 @@ * **状态结构**(parent state 模式): * - `idle`:屏没挂 / 登出 * - `guestSession`(**parent**):游客会话 —— **不** invoke WS - * - `initializing`:**双** invoke + `always` barrier - * - `loadQuotaActor`(**纯** local,ChatStorage **日**配 + 总**配**) - * - `loadHistoryActor`(local → network → save 3 步) - * - 2 **个** `quotaLoaded` / `historyLoaded` **都** true **才**进** ready - * - `ready`:用户聊天 + 看消息 - * - `sending`:invoke sendMessageHttpActor(HTTP 发送消息) - * - ~~`loadingMore`~~:**已**删**(游客**无**服**务**端** history,**不**支持翻**页**) * - `userSession`(**parent**):非游客会话 —— **invoke WS**(持久) - * - 父级 invoke `chatWebSocket`(**一**进**来****就**连) - * - `initializing`:invoke `loadHistory` + `always` barrier - * - WS **已**连** + history **加**载**完**成** → ready - * - `ready`:用户聊天 + 看消息 - * - `sending`:invoke sendMessageHttpActor(HTTP fallback —— WS 真发**未** wire) - * - `loadingMore`:invoke loadMoreHistory(**翻**页**,**保**留**) - * WS 在 `userSession` parent 状态 invoke —— 跨 initializing/ready/sending/loadingMore 持续 * * **init 双任务**(**核**心**重**构**): * - guestSession.initializing:loadQuota + loadHistory **并**行**(`invoke: [...]` **数**组**) @@ -42,20 +28,11 @@ * - 非游客"无消息限制"(业务事实)—— 永不被赋值 * - `appendUserMessage` 减 `context.wsConnected ? q : Math.max(0, q-1)` —— WS 已连时**不**递减 * - * **文件结构**: - * - `chat-machine.ts`(本文件)—— **只**含 setup + createMachine(actions + states) - * - `chat-machine.helpers.ts` —— 纯函数 + 数据加载(readGuestQuota + readAndSyncHistory) - * - `chat-machine.actors.ts` —— 5 个 XState actor(loadQuota + loadHistory + sendMessage + loadMoreHistory + chatWebSocket) - * - * **日**志**策略**(**清**理**后**): - * - **不**在**状**态**机**文**件**中**打**追踪**日**志**(actor / helper / action **本**身**有** ENTRY / DONE **日**志**,**就**够**排查**了) - * - `appendUserMessage` / `appendUserImage` / `appendOrUpdateAISentence` 这 3 **个** action `assign` **内**部**保**留** `console.log`(**记**录** action **本**身**的**业**务**决**策**,**比**如** quota 减**少** / isFromAI 标**记**) - * - **所**有** actor ENTRY / API / DONE **全**链**日**志**放**在** `chat-machine.actors.ts` - * - **所**有** helper 数据**加**载**日**志**(`readGuestQuota` / `readAndSyncHistory` 3 **步**)放**在** `chat-machine.helpers.ts` */ import { setup, assign } from "xstate"; +import { ChatStorage } from "@/data/storage/chat/chat_storage"; import { formatDate } from "@/utils/date"; import { ChatState, initialState } from "./chat-state"; @@ -98,19 +75,28 @@ export const chatMachine = setup({ appendUserMessage: assign(({ context, event }) => { if (event.type !== "ChatSendMessage") return {}; - const newQuota = context.wsConnected + // 两**个**额度**同**步**更**新**:WS 已**连****不**递减(**实**时**会**话**不**消耗**),HTTP **必**须**减 + const newRemaining = context.wsConnected ? context.guestRemainingQuota : Math.max(0, context.guestRemainingQuota - 1); + const newTotal = context.wsConnected + ? context.guestTotalQuota + : Math.max(0, context.guestTotalQuota - 1); + + // 持**久**化**两**个**额度**(** fire-and-forget,**不** await —— assign **是** sync) + // daily quota 需**要** today **字**符**串**(**让** ChatStorage 跨**天**判**断** reset) + const today = formatDate(); + ChatStorage.getInstance().setGuestDailyChatQuota(newRemaining, today); + ChatStorage.getInstance().setGuestTotalQuota(newTotal); console.log("[chat-machine] appendUserMessage", { contentLength: event.content.length, contentPreview: event.content.slice(0, 50), - oldMessagesCount: context.messages.length, - newMessagesCount: context.messages.length + 1, - wsConnected: context.wsConnected, quotaMode: context.wsConnected ? "ws (no decrement)" : "http (decrement)", - oldQuota: context.guestRemainingQuota, - newQuota, + oldRemaining: context.guestRemainingQuota, + newRemaining, + oldTotal: context.guestTotalQuota, + newTotal, isReplyingAI: true, }); @@ -120,25 +106,35 @@ export const chatMachine = setup({ { content: event.content, isFromAI: false, - date: formatDate(), + date: today, }, ], isReplyingAI: true, - guestRemainingQuota: newQuota, + guestRemainingQuota: newRemaining, + guestTotalQuota: newTotal, }; }), appendUserImage: assign(({ context, event }) => { if (event.type !== "ChatSendImage") return {}; - const newQuota = context.wsConnected + // **同** appendUserMessage:两**个**额度**同**步**减 + const newRemaining = context.wsConnected ? context.guestRemainingQuota : Math.max(0, context.guestRemainingQuota - 1); + const newTotal = context.wsConnected + ? context.guestTotalQuota + : Math.max(0, context.guestTotalQuota - 1); + const today = formatDate(); + void ChatStorage.getInstance().setGuestDailyChatQuota(newRemaining, today); + void ChatStorage.getInstance().setGuestTotalQuota(newTotal); console.log("[chat-machine] appendUserImage", { oldMessagesCount: context.messages.length, wsConnected: context.wsConnected, quotaMode: context.wsConnected ? "ws (no decrement)" : "http (decrement)", - oldQuota: context.guestRemainingQuota, - newQuota, + oldRemaining: context.guestRemainingQuota, + newRemaining, + oldTotal: context.guestTotalQuota, + newTotal, }); return { messages: [ @@ -146,12 +142,13 @@ export const chatMachine = setup({ { content: "[Image]", isFromAI: false, - date: formatDate(), + date: today, imageUrl: event.imageBase64, }, ], isReplyingAI: true, - guestRemainingQuota: newQuota, + guestRemainingQuota: newRemaining, + guestTotalQuota: newTotal, }; }), @@ -290,7 +287,6 @@ export const chatMachine = setup({ guard: ({ event }) => event.content.trim().length > 0, target: "sending", }, - // 兜**底**:**内**容**空** / 什**么**都**不**匹**配** → **静**默**丢**弃** ], // **配**额**检**查** + 发**送**图**片**(**同**样** 3 **条** guard,**不**检**查** "内**容**空"**) ChatSendImage: [