feat(chat): migrate chat protocol to lock detail

This commit is contained in:
2026-06-24 14:06:30 +08:00
parent c280a3c1ea
commit 42c03f8901
34 changed files with 609 additions and 373 deletions
+55 -39
View File
@@ -27,15 +27,15 @@
* - 每个任务都是独立 actor,不互相等待(除非 `always` barrier
*
* 消息发送路径(业务事实):
* - guestSession:走 HTTP,消息数量限制以后端 `blocked` 响应为准
* - nonVipUserSession:走 HTTP,让后端返回 daily_limit blocked
* - guestSession:走 HTTP,消息数量限制以后端 `lockDetail` 响应为准
* - nonVipUserSession:走 HTTP,让后端返回 daily_limit lockDetail
* - vipUserSession + WS 已连:走 WS,不受每日免费次数限制
* - `ChatWebSocketConnected` 事件 → `wsConnected = true`
* - `vipUserSession.exit` action → `wsConnected = false`WS actor cleanup 时也会跑 exit
*
* 配额:
* - 前端不再处理本地消息额度;游客 / 注册用户均以后端响应为准。
* - 后端返回 `blocked=true && blockReason="daily_limit"` 时展示会员引导。
* - 后端返回 `lockDetail.reason="daily_limit"` 且需要升级时展示会员引导。
*
*/
@@ -45,7 +45,10 @@ import { todayString, Logger } from "@/utils";
import { ChatState, initialState } from "./chat-state";
import type { ChatEvent } from "./chat-events";
import { applyHttpSendOutput } from "./chat-machine.helpers";
import {
applyHttpSendOutput,
normalizeLockDetail,
} from "./chat-machine.helpers";
import {
loadHistoryActor,
sendMessageHttpActor,
@@ -112,9 +115,10 @@ export const chatMachine = setup({
},
],
isReplyingAI: true,
paywallTriggered: false,
paywallReason: null,
paywallDetail: null,
upgradePromptVisible: false,
upgradeReason: null,
upgradeHint: null,
upgradeDetail: null,
};
}),
@@ -139,9 +143,10 @@ export const chatMachine = setup({
},
],
isReplyingAI: true,
paywallTriggered: false,
paywallReason: null,
paywallDetail: null,
upgradePromptVisible: false,
upgradeReason: null,
upgradeHint: null,
upgradeDetail: null,
};
}),
@@ -164,9 +169,10 @@ export const chatMachine = setup({
},
],
isReplyingAI: true,
paywallTriggered: false,
paywallReason: null,
paywallDetail: null,
upgradePromptVisible: false,
upgradeReason: null,
upgradeHint: null,
upgradeDetail: null,
};
}),
@@ -190,9 +196,10 @@ export const chatMachine = setup({
},
],
isReplyingAI: true,
paywallTriggered: false,
paywallReason: null,
paywallDetail: null,
upgradePromptVisible: false,
upgradeReason: null,
upgradeHint: null,
upgradeDetail: null,
};
}),
@@ -244,14 +251,14 @@ export const chatMachine = setup({
messages[messages.length - 1] = {
...last,
content: "",
imageUrl: event.imageUrl,
imageUrl: event.url,
};
} else {
messages.push({
content: "",
isFromAI: true,
date: todayString(),
imageUrl: event.imageUrl,
imageUrl: event.url,
});
}
@@ -262,25 +269,31 @@ export const chatMachine = setup({
return {
messages,
paywallTriggered: false,
paywallReason: null,
paywallDetail: null,
upgradePromptVisible: false,
upgradeReason: null,
upgradeHint: null,
upgradeDetail: null,
};
}),
applyPaywallStatus: assign(({ event }) => {
if (event.type !== "ChatPaywallStatusReceived") return {};
if (!event.showUpgrade) {
if (!event.lockDetail.showUpgrade) {
return {
paywallTriggered: false,
paywallReason: null,
paywallDetail: null,
upgradePromptVisible: false,
upgradeReason: null,
upgradeHint: null,
upgradeDetail: null,
};
}
return {
paywallTriggered: true,
paywallReason: "photo_paywall",
paywallDetail: null,
upgradePromptVisible: true,
upgradeReason:
event.lockDetail.reason === "private_message"
? "private_message"
: "image",
upgradeHint: event.lockDetail.hint,
upgradeDetail: normalizeLockDetail(event.lockDetail.detail),
};
}),
@@ -288,9 +301,10 @@ export const chatMachine = setup({
if (event.type !== "ChatUnlockPrivateMessage") return {};
return {
unlockingPrivateMessageId: event.messageId,
paywallTriggered: false,
paywallReason: null,
paywallDetail: null,
upgradePromptVisible: false,
upgradeReason: null,
upgradeHint: null,
upgradeDetail: null,
};
}),
@@ -309,25 +323,27 @@ export const chatMachine = setup({
? {
...message,
content: response.content ?? message.content,
privateLocked: false,
privateHint: null,
lockedPrivate: false,
privateMessageHint: null,
isPrivate: message.isPrivate ?? true,
}
: message,
),
unlockingPrivateMessageId: null,
paywallTriggered: false,
paywallReason: null,
paywallDetail: null,
upgradePromptVisible: false,
upgradeReason: null,
upgradeHint: null,
upgradeDetail: null,
};
}
if (response.showUpgrade) {
return {
unlockingPrivateMessageId: null,
paywallTriggered: true,
paywallReason: "private_paywall",
paywallDetail: {
upgradePromptVisible: true,
upgradeReason: "private_message",
upgradeHint: null,
upgradeDetail: {
usedToday: response.privateUsedToday,
limit: response.privateFreeLimit,
},