feat(chat): add promotional external entry flow

This commit is contained in:
2026-07-13 12:43:18 +08:00
parent e5ee9940ca
commit 3752b3b729
44 changed files with 1623 additions and 190 deletions
@@ -3,9 +3,23 @@
*/
import { z } from "zod";
export const UnlockPrivateRequestSchema = z.object({
messageId: z.string(),
});
export const ChatLockTypeSchema = z.enum([
"voice_message",
"image_paywall",
"private_message",
]);
export const UnlockPrivateRequestSchema = z
.object({
messageId: z.string().min(1).optional(),
lockType: ChatLockTypeSchema.optional(),
clientLockId: z.string().min(1).optional(),
})
.refine((value) => value.messageId || value.lockType, {
message: "messageId or lockType is required",
});
export type ChatLockType = z.output<typeof ChatLockTypeSchema>;
export type UnlockPrivateRequestInput = z.input<
typeof UnlockPrivateRequestSchema
@@ -1,24 +1,44 @@
import { z } from "zod";
import { booleanOrFalse, numberOrZero, stringOrEmpty } from "../nullable-defaults";
import { ChatLockDetailSchema } from "./chat_payloads";
import {
booleanOrFalse,
numberOrZero,
stringOrEmpty,
stringOrNull,
} from "../nullable-defaults";
import { ChatImageSchema, ChatLockDetailSchema } from "./chat_payloads";
import { ChatLockTypeSchema } from "./unlock_private_request";
/**
* 单条历史付费 / 私密消息解锁响应。
*/
export const UnlockPrivateReasonSchema = stringOrEmpty;
export const UnlockPrivateResponseSchema = z.object({
unlocked: booleanOrFalse,
content: stringOrEmpty,
audioUrl: stringOrEmpty,
reason: UnlockPrivateReasonSchema,
creditBalance: numberOrZero,
creditsCharged: numberOrZero,
requiredCredits: numberOrZero,
shortfallCredits: numberOrZero,
lockDetail: ChatLockDetailSchema,
});
export const UnlockPrivateResponseSchema = z
.object({
unlocked: booleanOrFalse,
content: stringOrEmpty,
reply: stringOrEmpty,
messageId: stringOrEmpty,
message_id: stringOrEmpty,
clientLockId: stringOrNull,
lockType: ChatLockTypeSchema.nullable().default(null),
audioUrl: stringOrEmpty,
audio_url: stringOrEmpty,
image: ChatImageSchema,
reason: UnlockPrivateReasonSchema,
creditBalance: numberOrZero,
creditsCharged: numberOrZero,
requiredCredits: numberOrZero,
shortfallCredits: numberOrZero,
lockDetail: ChatLockDetailSchema,
})
.transform(({ reply, message_id, audio_url, ...data }) => ({
...data,
content: data.content || reply,
messageId: data.messageId || message_id,
audioUrl: data.audioUrl || audio_url,
}));
export type UnlockPrivateReason = z.output<typeof UnlockPrivateReasonSchema>;
export type UnlockPrivateResponseInput = z.input<