refactor(chat): synchronize guest chat quota updates for messages and images

This commit is contained in:
2026-06-15 17:47:35 +08:00
parent 31f2433c4b
commit b9315c9f8b
+34 -38
View File
@@ -11,21 +11,7 @@
* **状态结构**parent state 模式):
* - `idle`:屏没挂 / 登出
* - `guestSession`**parent**):游客会话 —— **不** invoke WS
* - `initializing`**双** invoke + `always` barrier
* - `loadQuotaActor`**纯** localChatStorage **日**配 + 总**配**
* - `loadHistoryActor`local → network → save 3 步)
* - 2 **个** `quotaLoaded` / `historyLoaded` **都** true **才**进** ready
* - `ready`:用户聊天 + 看消息
* - `sending`invoke sendMessageHttpActorHTTP 发送消息)
* - ~~`loadingMore`~~**已**删**(游客**无**服**务**端** history**不**支持翻**页**
* - `userSession`**parent**):非游客会话 —— **invoke WS**(持久)
* - 父级 invoke `chatWebSocket`**一**进**来****就**连)
* - `initializing`invoke `loadHistory` + `always` barrier
* - WS **已**连** + history **加**载**完**成** → ready
* - `ready`:用户聊天 + 看消息
* - `sending`invoke sendMessageHttpActorHTTP fallback —— WS 真发**未** wire
* - `loadingMore`invoke loadMoreHistory**翻**页****保**留**
* WS 在 `userSession` parent 状态 invoke —— 跨 initializing/ready/sending/loadingMore 持续
*
* **init 双任务****核**心**重**构**):
* - guestSession.initializingloadQuota + loadHistory **并**行**`invoke: [...]` **数**组**
@@ -42,20 +28,11 @@
* - 非游客"无消息限制"(业务事实)—— 永不被赋值
* - `appendUserMessage` 减 `context.wsConnected ? q : Math.max(0, q-1)` —— WS 已连时**不**递减
*
* **文件结构**
* - `chat-machine.ts`(本文件)—— **只**含 setup + createMachineactions + states
* - `chat-machine.helpers.ts` —— 纯函数 + 数据加载(readGuestQuota + readAndSyncHistory
* - `chat-machine.actors.ts` —— 5 个 XState actorloadQuota + 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: [