fix(chat): handle guest total limit

This commit is contained in:
2026-06-23 15:14:17 +08:00
parent c4a33576bb
commit f14b8a48d2
5 changed files with 74 additions and 10 deletions
+10 -4
View File
@@ -43,10 +43,15 @@ export function ChatScreen() {
// isGuest 派生(chat-screen 是唯一知道这个值的地方 —— chat 机器无 isGuest 字段) // isGuest 派生(chat-screen 是唯一知道这个值的地方 —— chat 机器无 isGuest 字段)
const isGuest = deriveIsGuest(authState.loginStatus); const isGuest = deriveIsGuest(authState.loginStatus);
// 消息数量限制由后端统一返回 blocked/daily_limit,前端不再处理本地额度。 // 消息数量限制由后端统一返回 blocked,前端不再处理本地额度。
const showDailyLimitBanner = const showMessageLimitBanner =
state.paywallTriggered && state.paywallTriggered &&
state.paywallReason === "daily_limit"; (state.paywallReason === "daily_limit" ||
state.paywallReason === "total_limit");
const messageLimitTitle =
state.paywallReason === "total_limit"
? "The limit for free chat times\nhas been reached"
: undefined;
const showPhotoPaywallBanner = const showPhotoPaywallBanner =
state.paywallTriggered && state.paywallReason === "photo_paywall"; state.paywallTriggered && state.paywallReason === "photo_paywall";
const showPrivatePaywallBanner = const showPrivatePaywallBanner =
@@ -168,8 +173,9 @@ export function ChatScreen() {
/> />
) : null} ) : null}
{showDailyLimitBanner ? ( {showMessageLimitBanner ? (
<ChatQuotaExhaustedBanner <ChatQuotaExhaustedBanner
title={messageLimitTitle}
/> />
) : ( ) : (
<ChatInputBar disabled={state.isReplyingAI} /> <ChatInputBar disabled={state.isReplyingAI} />
@@ -0,0 +1,30 @@
{
"code": 200,
"message": "success",
"success": true,
"data": {
"mode": "http",
"type": "text",
"reply": "",
"voiceUrl": "",
"audioUrl": "",
"intimacyChange": 0,
"newIntimacy": 0,
"relationshipStage": "密友",
"currentMood": "happy",
"messageId": "",
"isGuest": true,
"timestamp": 1782197944984,
"blocked": true,
"blockReason": "total_limit",
"blockDetail": {
"type": "guest_total_msg_limit",
"usedTotal": 12,
"limit": 5
},
"paywallTriggered": false,
"showUpgrade": false,
"imageType": null,
"imageUrl": null
}
}
+2 -1
View File
@@ -6,7 +6,8 @@ import { z } from "zod";
export const ChatBlockDetailSchema = z.object({ export const ChatBlockDetailSchema = z.object({
type: z.string(), type: z.string(),
usedToday: z.number(), usedToday: z.number().optional(),
usedTotal: z.number().optional(),
limit: z.number(), limit: z.number(),
}); });
+18 -3
View File
@@ -103,7 +103,12 @@ export function applyHttpSendOutput(
): Partial<ChatState> { ): Partial<ChatState> {
const { response, reply } = output; const { response, reply } = output;
if (response.blocked === true && response.blockReason === "daily_limit") { const isMessageLimitBlocked =
response.blocked === true &&
(response.blockReason === "daily_limit" ||
response.blockReason === "total_limit");
if (isMessageLimitBlocked) {
const detail = response.blockDetail; const detail = response.blockDetail;
const lastMessage = context.messages[context.messages.length - 1]; const lastMessage = context.messages[context.messages.length - 1];
const messages = const messages =
@@ -115,9 +120,19 @@ export function applyHttpSendOutput(
messages, messages,
isReplyingAI: false, isReplyingAI: false,
paywallTriggered: true, paywallTriggered: true,
paywallReason: "daily_limit", paywallReason:
response.blockReason === "total_limit" ? "total_limit" : "daily_limit",
paywallDetail: detail paywallDetail: detail
? { usedToday: detail.usedToday, limit: detail.limit } ? {
type: detail.type,
...(detail.usedToday != null
? { usedToday: detail.usedToday }
: {}),
...(detail.usedTotal != null
? { usedTotal: detail.usedTotal }
: {}),
limit: detail.limit,
}
: null, : null,
}; };
} }
+14 -2
View File
@@ -19,8 +19,20 @@ export interface ChatState {
*/ */
wsConnected: boolean; wsConnected: boolean;
paywallTriggered: boolean; paywallTriggered: boolean;
paywallReason: "daily_limit" | "photo_paywall" | "private_paywall" | null; paywallReason:
paywallDetail: { usedToday: number; limit: number } | null; | "daily_limit"
| "total_limit"
| "photo_paywall"
| "private_paywall"
| null;
paywallDetail:
| {
type?: string;
usedToday?: number;
usedTotal?: number;
limit: number;
}
| null;
unlockingPrivateMessageId: string | null; unlockingPrivateMessageId: string | null;
isLoadingMore: boolean; isLoadingMore: boolean;
hasMore: boolean; hasMore: boolean;