refactor(dto): centralize backend schema defaults
This commit is contained in:
@@ -104,4 +104,22 @@ describe("UnlockPrivateResponse", () => {
|
|||||||
expect(response.requiredCredits).toBe(0);
|
expect(response.requiredCredits).toBe(0);
|
||||||
expect(response.shortfallCredits).toBe(0);
|
expect(response.shortfallCredits).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("defaults nullable lock detail through schema helpers", () => {
|
||||||
|
const response = UnlockPrivateResponse.from({
|
||||||
|
unlocked: false,
|
||||||
|
content: "",
|
||||||
|
reason: "insufficient_balance",
|
||||||
|
lockDetail: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.lockDetail).toEqual({
|
||||||
|
locked: false,
|
||||||
|
showContent: true,
|
||||||
|
showUpgrade: false,
|
||||||
|
reason: null,
|
||||||
|
hint: null,
|
||||||
|
detail: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -76,6 +76,22 @@ describe("User", () => {
|
|||||||
expect("dailyQuotas" in user.toJson()).toBe(false);
|
expect("dailyQuotas" in user.toJson()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("defaults nullable nested user fields through schema helpers", () => {
|
||||||
|
const user = User.fromJson({
|
||||||
|
id: "user-1",
|
||||||
|
username: "guest_user",
|
||||||
|
personalityTraits: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user.personalityTraits.toJson()).toEqual({
|
||||||
|
caring: 0.5,
|
||||||
|
playful: 0.5,
|
||||||
|
serious: 0.5,
|
||||||
|
cheerful: 0.5,
|
||||||
|
romantic: 0.5,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("parses the latest backend entitlements payload", () => {
|
it("parses the latest backend entitlements payload", () => {
|
||||||
const entitlements = UserEntitlements.fromJson({
|
const entitlements = UserEntitlements.fromJson({
|
||||||
userId: "user-1",
|
userId: "user-1",
|
||||||
@@ -122,4 +138,29 @@ describe("User", () => {
|
|||||||
expect(entitlements.creditBalance).toBe(120);
|
expect(entitlements.creditBalance).toBe(120);
|
||||||
expect(entitlements.historyUnlock.costs.photo).toBe(40);
|
expect(entitlements.historyUnlock.costs.photo).toBe(40);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("defaults nullable entitlement sections through schema helpers", () => {
|
||||||
|
const entitlements = UserEntitlements.fromJson({
|
||||||
|
userId: "user-1",
|
||||||
|
policy: null,
|
||||||
|
costs: null,
|
||||||
|
quotas: null,
|
||||||
|
historyUnlock: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(entitlements.policy.membershipState).toBe("");
|
||||||
|
expect(entitlements.costs.private_message).toBe(0);
|
||||||
|
expect(entitlements.quotas.normalChatFreeDaily).toBe(0);
|
||||||
|
expect(entitlements.historyUnlock).toMatchObject({
|
||||||
|
enabled: false,
|
||||||
|
order: "",
|
||||||
|
chargeMode: "",
|
||||||
|
insufficientBalanceBehavior: "",
|
||||||
|
costs: {
|
||||||
|
private_message: 0,
|
||||||
|
voice_message: 0,
|
||||||
|
photo: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,10 +4,12 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { booleanOrFalse } from "../nullable-defaults";
|
||||||
|
|
||||||
export const AppleLoginRequestSchema = z.object({
|
export const AppleLoginRequestSchema = z.object({
|
||||||
identityToken: z.string(),
|
identityToken: z.string(),
|
||||||
platform: z.string(),
|
platform: z.string(),
|
||||||
isTestAccount: z.boolean().default(false),
|
isTestAccount: booleanOrFalse,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type AppleLoginRequestInput = z.input<typeof AppleLoginRequestSchema>;
|
export type AppleLoginRequestInput = z.input<typeof AppleLoginRequestSchema>;
|
||||||
|
|||||||
@@ -4,11 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { booleanOrFalse, stringOrEmpty } from "../nullable-defaults";
|
||||||
|
|
||||||
export const FacebookLoginRequestSchema = z.object({
|
export const FacebookLoginRequestSchema = z.object({
|
||||||
accessToken: z.string(),
|
accessToken: z.string(),
|
||||||
platform: z.string(),
|
platform: z.string(),
|
||||||
guestId: z.string().default(""),
|
guestId: stringOrEmpty,
|
||||||
isTestAccount: z.boolean().default(false),
|
isTestAccount: booleanOrFalse,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type FacebookLoginRequestInput = z.input<typeof FacebookLoginRequestSchema>;
|
export type FacebookLoginRequestInput = z.input<typeof FacebookLoginRequestSchema>;
|
||||||
|
|||||||
@@ -6,11 +6,13 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { stringOrNull } from "../nullable-defaults";
|
||||||
|
|
||||||
export const FacebookUserDataSchema = z.object({
|
export const FacebookUserDataSchema = z.object({
|
||||||
id: z.string().nullable().default(null),
|
id: stringOrNull,
|
||||||
name: z.string().nullable().default(null),
|
name: stringOrNull,
|
||||||
email: z.string().nullable().default(null),
|
email: stringOrNull,
|
||||||
pictureUrl: z.string().nullable().default(null),
|
pictureUrl: stringOrNull,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type FacebookUserDataInput = z.input<typeof FacebookUserDataSchema>;
|
export type FacebookUserDataInput = z.input<typeof FacebookUserDataSchema>;
|
||||||
|
|||||||
@@ -4,10 +4,12 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { booleanOrFalse, stringOrEmpty } from "../nullable-defaults";
|
||||||
|
|
||||||
export const FbIdLoginRequestSchema = z.object({
|
export const FbIdLoginRequestSchema = z.object({
|
||||||
fbId: z.string(),
|
fbId: z.string(),
|
||||||
avatarUrl: z.string().default(""),
|
avatarUrl: stringOrEmpty,
|
||||||
isTestAccount: z.boolean().default(false),
|
isTestAccount: booleanOrFalse,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type FbIdLoginRequestInput = z.input<typeof FbIdLoginRequestSchema>;
|
export type FbIdLoginRequestInput = z.input<typeof FbIdLoginRequestSchema>;
|
||||||
|
|||||||
@@ -4,11 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { booleanOrFalse, stringOrEmpty } from "../nullable-defaults";
|
||||||
|
|
||||||
export const GoogleLoginRequestSchema = z.object({
|
export const GoogleLoginRequestSchema = z.object({
|
||||||
idToken: z.string(),
|
idToken: z.string(),
|
||||||
platform: z.string(),
|
platform: z.string(),
|
||||||
guestId: z.string().default(""),
|
guestId: stringOrEmpty,
|
||||||
isTestAccount: z.boolean().default(false),
|
isTestAccount: booleanOrFalse,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type GoogleLoginRequestInput = z.input<typeof GoogleLoginRequestSchema>;
|
export type GoogleLoginRequestInput = z.input<typeof GoogleLoginRequestSchema>;
|
||||||
|
|||||||
@@ -4,9 +4,11 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { booleanOrFalse } from "../nullable-defaults";
|
||||||
|
|
||||||
export const GuestLoginRequestSchema = z.object({
|
export const GuestLoginRequestSchema = z.object({
|
||||||
deviceId: z.string(),
|
deviceId: z.string(),
|
||||||
isTestAccount: z.boolean().default(false),
|
isTestAccount: booleanOrFalse,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type GuestLoginRequestInput = z.input<typeof GuestLoginRequestSchema>;
|
export type GuestLoginRequestInput = z.input<typeof GuestLoginRequestSchema>;
|
||||||
|
|||||||
@@ -3,15 +3,17 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { booleanOrTrue, numberOr, schemaOrNull } from "../nullable-defaults";
|
||||||
import { UserSchema } from "../user/user";
|
import { UserSchema } from "../user/user";
|
||||||
|
|
||||||
export const GuestLoginResponseSchema = z.object({
|
export const GuestLoginResponseSchema = z.object({
|
||||||
token: z.string(),
|
token: z.string(),
|
||||||
deviceId: z.string(),
|
deviceId: z.string(),
|
||||||
userId: z.string(),
|
userId: z.string(),
|
||||||
isDeviceUser: z.boolean().default(true),
|
isDeviceUser: booleanOrTrue,
|
||||||
expiresIn: z.number().default(2592000),
|
expiresIn: numberOr(2592000),
|
||||||
user: UserSchema.optional(),
|
user: schemaOrNull(UserSchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type GuestLoginResponseInput = z.input<typeof GuestLoginResponseSchema>;
|
export type GuestLoginResponseInput = z.input<typeof GuestLoginResponseSchema>;
|
||||||
|
|||||||
@@ -4,13 +4,15 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { booleanOrFalse, stringOrEmpty } from "../nullable-defaults";
|
||||||
|
|
||||||
export const LoginRequestSchema = z.object({
|
export const LoginRequestSchema = z.object({
|
||||||
email: z.string().default(""),
|
email: stringOrEmpty,
|
||||||
username: z.string().default(""),
|
username: stringOrEmpty,
|
||||||
password: z.string(),
|
password: z.string(),
|
||||||
platform: z.string(),
|
platform: z.string(),
|
||||||
guestId: z.string().default(""),
|
guestId: stringOrEmpty,
|
||||||
isTestAccount: z.boolean().default(false),
|
isTestAccount: booleanOrFalse,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type LoginRequestInput = z.input<typeof LoginRequestSchema>;
|
export type LoginRequestInput = z.input<typeof LoginRequestSchema>;
|
||||||
|
|||||||
@@ -3,12 +3,14 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { stringOrEmpty } from "../nullable-defaults";
|
||||||
import { UserSchema } from "../user/user";
|
import { UserSchema } from "../user/user";
|
||||||
|
|
||||||
export const LoginResponseSchema = z.object({
|
export const LoginResponseSchema = z.object({
|
||||||
user: UserSchema,
|
user: UserSchema,
|
||||||
token: z.string(),
|
token: z.string(),
|
||||||
refreshToken: z.string().default(""),
|
refreshToken: stringOrEmpty,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type LoginResponseInput = z.input<typeof LoginResponseSchema>;
|
export type LoginResponseInput = z.input<typeof LoginResponseSchema>;
|
||||||
|
|||||||
@@ -4,8 +4,10 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { booleanOrTrue } from "../nullable-defaults";
|
||||||
|
|
||||||
export const LogoutResponseSchema = z.object({
|
export const LogoutResponseSchema = z.object({
|
||||||
success: z.boolean().default(true),
|
success: booleanOrTrue,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type LogoutResponseInput = z.input<typeof LogoutResponseSchema>;
|
export type LogoutResponseInput = z.input<typeof LogoutResponseSchema>;
|
||||||
|
|||||||
@@ -4,13 +4,15 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { booleanOrFalse, stringOrEmpty } from "../nullable-defaults";
|
||||||
|
|
||||||
export const RegisterRequestSchema = z.object({
|
export const RegisterRequestSchema = z.object({
|
||||||
username: z.string(),
|
username: z.string(),
|
||||||
email: z.string(),
|
email: z.string(),
|
||||||
password: z.string(),
|
password: z.string(),
|
||||||
platform: z.string(),
|
platform: z.string(),
|
||||||
guestId: z.string().default(""),
|
guestId: stringOrEmpty,
|
||||||
isTestAccount: z.boolean().default(false),
|
isTestAccount: booleanOrFalse,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type RegisterRequestInput = z.input<typeof RegisterRequestSchema>;
|
export type RegisterRequestInput = z.input<typeof RegisterRequestSchema>;
|
||||||
|
|||||||
@@ -3,17 +3,19 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { arrayOrEmpty, booleanOrFalse, numberOrZero } from "../nullable-defaults";
|
||||||
import { ChatMessageSchema } from "./chat_message";
|
import { ChatMessageSchema } from "./chat_message";
|
||||||
|
|
||||||
export const ChatHistoryResponseSchema = z.object({
|
export const ChatHistoryResponseSchema = z.object({
|
||||||
messages: z.array(ChatMessageSchema),
|
messages: arrayOrEmpty(ChatMessageSchema),
|
||||||
total: z.number().default(0),
|
total: numberOrZero,
|
||||||
limit: z.number(),
|
limit: numberOrZero,
|
||||||
offset: z.number(),
|
offset: numberOrZero,
|
||||||
isVip: z.boolean().default(false),
|
isVip: booleanOrFalse,
|
||||||
privateFreeLimit: z.number().default(0),
|
privateFreeLimit: numberOrZero,
|
||||||
privateUsedToday: z.number().default(0),
|
privateUsedToday: numberOrZero,
|
||||||
privateCanViewFree: z.boolean().default(false),
|
privateCanViewFree: booleanOrFalse,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type ChatHistoryResponseInput = z.input<typeof ChatHistoryResponseSchema>;
|
export type ChatHistoryResponseInput = z.input<typeof ChatHistoryResponseSchema>;
|
||||||
|
|||||||
@@ -3,17 +3,19 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { stringOr, stringOrEmpty, stringOrNull } from "../nullable-defaults";
|
||||||
import { ChatImageSchema, ChatLockDetailSchema } from "./chat_payloads";
|
import { ChatImageSchema, ChatLockDetailSchema } from "./chat_payloads";
|
||||||
|
|
||||||
export const ChatMessageSchema = z
|
export const ChatMessageSchema = z
|
||||||
.object({
|
.object({
|
||||||
role: z.string(),
|
role: stringOrEmpty,
|
||||||
type: z.string().default("text"),
|
type: stringOr("text"),
|
||||||
content: z.string(),
|
content: stringOrEmpty,
|
||||||
id: z.string().default(""),
|
id: stringOrEmpty,
|
||||||
createdAt: z.string().default(""),
|
createdAt: stringOrEmpty,
|
||||||
created_at: z.string().optional(),
|
created_at: stringOrNull,
|
||||||
audioUrl: z.string().nullable().default(null),
|
audioUrl: stringOrNull,
|
||||||
image: ChatImageSchema,
|
image: ChatImageSchema,
|
||||||
lockDetail: ChatLockDetailSchema,
|
lockDetail: ChatLockDetailSchema,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3,30 +3,44 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
export const ChatImageSchema = z
|
import {
|
||||||
.object({
|
booleanOrFalse,
|
||||||
type: z.string().nullable().default(null),
|
booleanOrTrue,
|
||||||
url: z.string().nullable().default(null),
|
schemaOr,
|
||||||
})
|
stringOrNull,
|
||||||
.default({ type: null, url: null });
|
unknownRecordOrNull,
|
||||||
|
} from "../nullable-defaults";
|
||||||
|
|
||||||
export const ChatLockDetailSchema = z
|
const CHAT_IMAGE_DEFAULTS = { type: null, url: null } as const;
|
||||||
.object({
|
|
||||||
locked: z.boolean().default(false),
|
const CHAT_LOCK_DETAIL_DEFAULTS = {
|
||||||
showContent: z.boolean().default(true),
|
|
||||||
showUpgrade: z.boolean().default(false),
|
|
||||||
reason: z.string().nullable().default(null),
|
|
||||||
hint: z.string().nullable().default(null),
|
|
||||||
detail: z.record(z.string(), z.unknown()).nullable().default(null),
|
|
||||||
})
|
|
||||||
.default({
|
|
||||||
locked: false,
|
locked: false,
|
||||||
showContent: true,
|
showContent: true,
|
||||||
showUpgrade: false,
|
showUpgrade: false,
|
||||||
reason: null,
|
reason: null,
|
||||||
hint: null,
|
hint: null,
|
||||||
detail: null,
|
detail: null,
|
||||||
});
|
} as const;
|
||||||
|
|
||||||
|
export const ChatImageSchema = schemaOr(
|
||||||
|
z.object({
|
||||||
|
type: stringOrNull,
|
||||||
|
url: stringOrNull,
|
||||||
|
}),
|
||||||
|
CHAT_IMAGE_DEFAULTS,
|
||||||
|
);
|
||||||
|
|
||||||
|
export const ChatLockDetailSchema = schemaOr(
|
||||||
|
z.object({
|
||||||
|
locked: booleanOrFalse,
|
||||||
|
showContent: booleanOrTrue,
|
||||||
|
showUpgrade: booleanOrFalse,
|
||||||
|
reason: stringOrNull,
|
||||||
|
hint: stringOrNull,
|
||||||
|
detail: unknownRecordOrNull,
|
||||||
|
}),
|
||||||
|
CHAT_LOCK_DETAIL_DEFAULTS,
|
||||||
|
);
|
||||||
|
|
||||||
export type ChatImageInput = z.input<typeof ChatImageSchema>;
|
export type ChatImageInput = z.input<typeof ChatImageSchema>;
|
||||||
export type ChatImageData = z.output<typeof ChatImageSchema>;
|
export type ChatImageData = z.output<typeof ChatImageSchema>;
|
||||||
|
|||||||
@@ -3,19 +3,26 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { booleanOrFalse, numberOrZero, stringOrNull } from "../nullable-defaults";
|
import {
|
||||||
|
booleanOrFalse,
|
||||||
|
booleanOrTrue,
|
||||||
|
numberOrLazy,
|
||||||
|
numberOrZero,
|
||||||
|
stringOrEmpty,
|
||||||
|
stringOrNull,
|
||||||
|
} from "../nullable-defaults";
|
||||||
import { ChatImageSchema, ChatLockDetailSchema } from "./chat_payloads";
|
import { ChatImageSchema, ChatLockDetailSchema } from "./chat_payloads";
|
||||||
|
|
||||||
export const ChatSendResponseSchema = z.object({
|
export const ChatSendResponseSchema = z.object({
|
||||||
reply: z.string(),
|
reply: stringOrEmpty,
|
||||||
audioUrl: z.string().default(""),
|
audioUrl: stringOrEmpty,
|
||||||
messageId: z.string(),
|
messageId: stringOrEmpty,
|
||||||
isGuest: booleanOrFalse,
|
isGuest: booleanOrFalse,
|
||||||
// 函数式 default —— 每次 parse 时重新调 Date.now()(后端不返回 timestamp 时用当下时间)
|
// 函数式 default —— 每次 parse 时重新调 Date.now()(后端不返回 timestamp 时用当下时间)
|
||||||
timestamp: z.number().default(() => Date.now()),
|
timestamp: numberOrLazy(() => Date.now()),
|
||||||
image: ChatImageSchema,
|
image: ChatImageSchema,
|
||||||
lockDetail: ChatLockDetailSchema,
|
lockDetail: ChatLockDetailSchema,
|
||||||
canSendMessage: z.boolean().default(true),
|
canSendMessage: booleanOrTrue,
|
||||||
cannotSendReason: stringOrNull,
|
cannotSendReason: stringOrNull,
|
||||||
creditBalance: numberOrZero,
|
creditBalance: numberOrZero,
|
||||||
creditsCharged: numberOrZero,
|
creditsCharged: numberOrZero,
|
||||||
|
|||||||
@@ -4,9 +4,11 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { numberOrZero } from "../nullable-defaults";
|
||||||
|
|
||||||
export const ChatSyncDataSchema = z.object({
|
export const ChatSyncDataSchema = z.object({
|
||||||
syncedCount: z.number(),
|
syncedCount: numberOrZero,
|
||||||
totalHistory: z.number(),
|
totalHistory: numberOrZero,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type ChatSyncDataInput = z.input<typeof ChatSyncDataSchema>;
|
export type ChatSyncDataInput = z.input<typeof ChatSyncDataSchema>;
|
||||||
|
|||||||
@@ -4,15 +4,17 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { booleanOrTrue, numberOrZero, stringOrEmpty } from "../nullable-defaults";
|
||||||
|
|
||||||
export const ImageUploadResponseSchema = z.object({
|
export const ImageUploadResponseSchema = z.object({
|
||||||
success: z.boolean().default(true),
|
success: booleanOrTrue,
|
||||||
imageId: z.string(),
|
imageId: stringOrEmpty,
|
||||||
thumbUrl: z.string(),
|
thumbUrl: stringOrEmpty,
|
||||||
mediumUrl: z.string(),
|
mediumUrl: stringOrEmpty,
|
||||||
originalUrl: z.string(),
|
originalUrl: stringOrEmpty,
|
||||||
width: z.number(),
|
width: numberOrZero,
|
||||||
height: z.number(),
|
height: numberOrZero,
|
||||||
bytes: z.number(),
|
bytes: numberOrZero,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type ImageUploadResponseInput = z.input<typeof ImageUploadResponseSchema>;
|
export type ImageUploadResponseInput = z.input<typeof ImageUploadResponseSchema>;
|
||||||
|
|||||||
@@ -4,16 +4,18 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { booleanOrFalse, numberOrZero, stringOrEmpty } from "../nullable-defaults";
|
||||||
|
|
||||||
export const SendMessageRequestSchema = z.object({
|
export const SendMessageRequestSchema = z.object({
|
||||||
message: z.string().default(""),
|
message: stringOrEmpty,
|
||||||
image: z.string().default(""),
|
image: stringOrEmpty,
|
||||||
imageId: z.string().default(""),
|
imageId: stringOrEmpty,
|
||||||
imageThumbUrl: z.string().default(""),
|
imageThumbUrl: stringOrEmpty,
|
||||||
imageMediumUrl: z.string().default(""),
|
imageMediumUrl: stringOrEmpty,
|
||||||
imageOriginalUrl: z.string().default(""),
|
imageOriginalUrl: stringOrEmpty,
|
||||||
imageWidth: z.number().default(0),
|
imageWidth: numberOrZero,
|
||||||
imageHeight: z.number().default(0),
|
imageHeight: numberOrZero,
|
||||||
useWebSocket: z.boolean().default(false),
|
useWebSocket: booleanOrFalse,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type SendMessageRequestInput = z.input<typeof SendMessageRequestSchema>;
|
export type SendMessageRequestInput = z.input<typeof SendMessageRequestSchema>;
|
||||||
|
|||||||
@@ -4,8 +4,10 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { stringOrEmpty } from "../nullable-defaults";
|
||||||
|
|
||||||
export const SttDataSchema = z.object({
|
export const SttDataSchema = z.object({
|
||||||
text: z.string(),
|
text: stringOrEmpty,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type SttDataInput = z.input<typeof SttDataSchema>;
|
export type SttDataInput = z.input<typeof SttDataSchema>;
|
||||||
|
|||||||
@@ -4,10 +4,12 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { stringOrEmpty } from "../nullable-defaults";
|
||||||
|
|
||||||
export const SyncMessageSchema = z.object({
|
export const SyncMessageSchema = z.object({
|
||||||
role: z.string(),
|
role: stringOrEmpty,
|
||||||
content: z.string(),
|
content: stringOrEmpty,
|
||||||
timestamp: z.string(),
|
timestamp: stringOrEmpty,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type SyncMessageInput = z.input<typeof SyncMessageSchema>;
|
export type SyncMessageInput = z.input<typeof SyncMessageSchema>;
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
export const UnlockHistoryCostsByMessageSchema = z.record(z.string(), z.number());
|
import { arrayOrEmpty, booleanOrFalse, numberOrZero, recordOrEmpty } from "../nullable-defaults";
|
||||||
|
|
||||||
|
export const UnlockHistoryCostsByMessageSchema = recordOrEmpty(z.number());
|
||||||
|
|
||||||
export const UnlockHistoryReasonSchema = z.enum([
|
export const UnlockHistoryReasonSchema = z.enum([
|
||||||
"ok",
|
"ok",
|
||||||
@@ -12,19 +14,19 @@ export const UnlockHistoryReasonSchema = z.enum([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
export const UnlockHistoryResponseSchema = z.object({
|
export const UnlockHistoryResponseSchema = z.object({
|
||||||
unlocked: z.boolean(),
|
unlocked: booleanOrFalse,
|
||||||
reason: UnlockHistoryReasonSchema,
|
reason: UnlockHistoryReasonSchema,
|
||||||
totalLocked: z.number().default(0),
|
totalLocked: numberOrZero,
|
||||||
unlockedCount: z.number().default(0),
|
unlockedCount: numberOrZero,
|
||||||
privateCount: z.number().default(0),
|
privateCount: numberOrZero,
|
||||||
imageCount: z.number().default(0),
|
imageCount: numberOrZero,
|
||||||
voiceCount: z.number().default(0),
|
voiceCount: numberOrZero,
|
||||||
requiredCredits: z.number().default(0),
|
requiredCredits: numberOrZero,
|
||||||
currentCredits: z.number().default(0),
|
currentCredits: numberOrZero,
|
||||||
remainingCredits: z.number().default(0),
|
remainingCredits: numberOrZero,
|
||||||
shortfallCredits: z.number().default(0),
|
shortfallCredits: numberOrZero,
|
||||||
costsByMessage: UnlockHistoryCostsByMessageSchema.default({}),
|
costsByMessage: UnlockHistoryCostsByMessageSchema,
|
||||||
messageIds: z.array(z.string()).default([]),
|
messageIds: arrayOrEmpty(z.string()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type UnlockHistoryReason = z.output<typeof UnlockHistoryReasonSchema>;
|
export type UnlockHistoryReason = z.output<typeof UnlockHistoryReasonSchema>;
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { numberOrZero, stringOrEmpty } from "../nullable-defaults";
|
import { booleanOrFalse, numberOrZero, stringOrEmpty } from "../nullable-defaults";
|
||||||
import { ChatLockDetailSchema } from "./chat_payloads";
|
import { ChatLockDetailSchema } from "./chat_payloads";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 单条历史付费 / 私密消息解锁响应。
|
* 单条历史付费 / 私密消息解锁响应。
|
||||||
*/
|
*/
|
||||||
export const UnlockPrivateReasonSchema = z.string().default("");
|
export const UnlockPrivateReasonSchema = stringOrEmpty;
|
||||||
|
|
||||||
export const UnlockPrivateResponseSchema = z.object({
|
export const UnlockPrivateResponseSchema = z.object({
|
||||||
unlocked: z.boolean(),
|
unlocked: booleanOrFalse,
|
||||||
content: stringOrEmpty,
|
content: stringOrEmpty,
|
||||||
reason: UnlockPrivateReasonSchema,
|
reason: UnlockPrivateReasonSchema,
|
||||||
creditBalance: numberOrZero,
|
creditBalance: numberOrZero,
|
||||||
|
|||||||
@@ -4,10 +4,12 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { stringOrEmpty } from "../nullable-defaults";
|
||||||
|
|
||||||
export const AppEventSchema = z.object({
|
export const AppEventSchema = z.object({
|
||||||
userId: z.string(),
|
userId: stringOrEmpty,
|
||||||
browser: z.string(),
|
browser: stringOrEmpty,
|
||||||
userAgent: z.string(),
|
userAgent: stringOrEmpty,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type AppEventInput = z.input<typeof AppEventSchema>;
|
export type AppEventInput = z.input<typeof AppEventSchema>;
|
||||||
|
|||||||
@@ -4,12 +4,18 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import {
|
||||||
|
booleanOrFalse,
|
||||||
|
numberOrZero,
|
||||||
|
stringOrEmpty,
|
||||||
|
} from "../nullable-defaults";
|
||||||
|
|
||||||
export const PwaEventSchema = z.object({
|
export const PwaEventSchema = z.object({
|
||||||
deviceId: z.string(),
|
deviceId: stringOrEmpty,
|
||||||
deviceType: z.string(),
|
deviceType: stringOrEmpty,
|
||||||
timestamp: z.number(),
|
timestamp: numberOrZero,
|
||||||
pwaInstalled: z.boolean().default(false),
|
pwaInstalled: booleanOrFalse,
|
||||||
pwaSupported: z.boolean().default(false),
|
pwaSupported: booleanOrFalse,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type PwaEventInput = z.input<typeof PwaEventSchema>;
|
export type PwaEventInput = z.input<typeof PwaEventSchema>;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Zod 默认值辅助器(处理 `null | undefined` → 标量默认)
|
* Zod 默认值辅助器(处理 `null | undefined` → 默认值)
|
||||||
*
|
*
|
||||||
* 防御性解析:后端对 Facebook / Apple 等 OAuth 用户可能返回 `null`(如 `email: null`),
|
* 防御性解析:后端对 Facebook / Apple 等 OAuth 用户可能返回 `null`(如 `email: null`),
|
||||||
* Zod 默认的 `.default("")` 只接受 `undefined`,遇到 `null` 会抛 ZodError → 登录静默失败。
|
* Zod 默认的 `.default("")` 只接受 `undefined`,遇到 `null` 会抛 ZodError → 登录静默失败。
|
||||||
@@ -11,26 +11,131 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
/** `string | null | undefined` → `string`(默认空串) */
|
type SchemaDefault<T extends z.ZodType> = Exclude<z.output<T>, undefined>;
|
||||||
export const stringOrEmpty = z
|
|
||||||
|
/** `string | null | undefined` → `string` */
|
||||||
|
export function stringOr(defaultValue: string) {
|
||||||
|
return z
|
||||||
.string()
|
.string()
|
||||||
.nullable()
|
.nullable()
|
||||||
.transform((v) => v ?? "")
|
.transform((v) => v ?? defaultValue)
|
||||||
.default("");
|
.default(defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** `string | null | undefined` → `string`(默认空串) */
|
||||||
|
export const stringOrEmpty = stringOr("");
|
||||||
|
|
||||||
|
/** required `string | null` → `string`(缺字段仍报错,null 收敛为空串) */
|
||||||
|
export const requiredStringOrEmpty = z
|
||||||
|
.string()
|
||||||
|
.nullable()
|
||||||
|
.transform((v) => v ?? "");
|
||||||
|
|
||||||
/** `string | null | undefined` → `string | null`(默认 null) */
|
/** `string | null | undefined` → `string | null`(默认 null) */
|
||||||
export const stringOrNull = z.string().nullable().default(null);
|
export const stringOrNull = z.string().nullable().default(null);
|
||||||
|
|
||||||
/** `number | null | undefined` → `number`(默认 0) */
|
/** `number | null | undefined` → `number` */
|
||||||
export const numberOrZero = z
|
export function numberOr(defaultValue: number) {
|
||||||
|
return z
|
||||||
.number()
|
.number()
|
||||||
.nullable()
|
.nullable()
|
||||||
.transform((v) => v ?? 0)
|
.transform((v) => v ?? defaultValue)
|
||||||
.default(0);
|
.default(defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
/** `boolean | null | undefined` → `boolean`(默认 false) */
|
/** `number | null | undefined` → `number`,默认值在 parse 时惰性计算 */
|
||||||
export const booleanOrFalse = z
|
export function numberOrLazy(defaultValue: () => number) {
|
||||||
|
return z
|
||||||
|
.number()
|
||||||
|
.nullable()
|
||||||
|
.transform((v) => v ?? defaultValue())
|
||||||
|
.default(defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** `number | null | undefined` → `number`(默认 0) */
|
||||||
|
export const numberOrZero = numberOr(0);
|
||||||
|
|
||||||
|
/** required `number | null` → `number`(缺字段仍报错,null 收敛为 0) */
|
||||||
|
export const requiredNumberOrZero = z
|
||||||
|
.number()
|
||||||
|
.nullable()
|
||||||
|
.transform((v) => v ?? 0);
|
||||||
|
|
||||||
|
/** `number | null | undefined` → `number | null`(默认 null) */
|
||||||
|
export const numberOrNull = z.number().nullable().default(null);
|
||||||
|
|
||||||
|
/** `boolean | null | undefined` → `boolean` */
|
||||||
|
export function booleanOr(defaultValue: boolean) {
|
||||||
|
return z
|
||||||
.boolean()
|
.boolean()
|
||||||
.nullable()
|
.nullable()
|
||||||
.transform((v) => v ?? false)
|
.transform((v) => v ?? defaultValue)
|
||||||
.default(false);
|
.default(defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** `boolean | null | undefined` → `boolean`(默认 false) */
|
||||||
|
export const booleanOrFalse = booleanOr(false);
|
||||||
|
|
||||||
|
/** `boolean | null | undefined` → `boolean`(默认 true) */
|
||||||
|
export const booleanOrTrue = booleanOr(true);
|
||||||
|
|
||||||
|
/** required `boolean | null` → `boolean`(缺字段仍报错,null 收敛为 false) */
|
||||||
|
export const requiredBooleanOrFalse = z
|
||||||
|
.boolean()
|
||||||
|
.nullable()
|
||||||
|
.transform((v) => v ?? false);
|
||||||
|
|
||||||
|
/** `T[] | null | undefined` → `T[]`(默认空数组) */
|
||||||
|
export function arrayOrEmpty<T extends z.ZodType>(schema: T) {
|
||||||
|
return z
|
||||||
|
.array(schema)
|
||||||
|
.nullable()
|
||||||
|
.transform((v) => v ?? [])
|
||||||
|
.default(() => []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** `Record<string, T> | null | undefined` → `Record<string, T>`(默认空对象) */
|
||||||
|
export function recordOrEmpty<T extends z.ZodType>(schema: T) {
|
||||||
|
return z
|
||||||
|
.record(z.string(), schema)
|
||||||
|
.nullable()
|
||||||
|
.transform((v) => v ?? {})
|
||||||
|
.default(() => ({}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** `Record<string, unknown> | null | undefined` → `Record<string, unknown>` */
|
||||||
|
export const unknownRecordOrEmpty = recordOrEmpty(z.unknown());
|
||||||
|
|
||||||
|
/** `Record<string, unknown> | null | undefined` → `Record<string, unknown> | null` */
|
||||||
|
export const unknownRecordOrNull = z
|
||||||
|
.record(z.string(), z.unknown())
|
||||||
|
.nullable()
|
||||||
|
.default(null);
|
||||||
|
|
||||||
|
function resolveDefault<T>(defaultValue: T | (() => T)): T {
|
||||||
|
return typeof defaultValue === "function"
|
||||||
|
? (defaultValue as () => T)()
|
||||||
|
: defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** `T | null | undefined` → `T`(默认对象会继续经过 schema 解析) */
|
||||||
|
export function schemaOr<T extends z.ZodType>(
|
||||||
|
schema: T,
|
||||||
|
defaultValue: SchemaDefault<T> | (() => SchemaDefault<T>),
|
||||||
|
) {
|
||||||
|
const preprocessed = z.preprocess(
|
||||||
|
(value) => value ?? resolveDefault(defaultValue),
|
||||||
|
schema,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (typeof defaultValue === "function") {
|
||||||
|
return preprocessed.default(defaultValue as () => SchemaDefault<T>);
|
||||||
|
}
|
||||||
|
|
||||||
|
return preprocessed.default(defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** `T | null | undefined` → `T | null`(默认 null) */
|
||||||
|
export function schemaOrNull<T extends z.ZodType>(schema: T) {
|
||||||
|
return schema.nullable().default(null);
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { stringOrNull } from "../nullable-defaults";
|
||||||
|
|
||||||
const paymentUrlKeys = [
|
const paymentUrlKeys = [
|
||||||
"cashierUrl",
|
"cashierUrl",
|
||||||
"cashier_url",
|
"cashier_url",
|
||||||
@@ -20,17 +22,17 @@ const paymentUrlKeys = [
|
|||||||
export const CreatePaymentOrderResponseSchema = z.object({
|
export const CreatePaymentOrderResponseSchema = z.object({
|
||||||
orderId: z.string(),
|
orderId: z.string(),
|
||||||
payParams: z.record(z.string(), z.unknown()),
|
payParams: z.record(z.string(), z.unknown()),
|
||||||
cashierUrl: z.string().optional(),
|
cashierUrl: stringOrNull,
|
||||||
cashier_url: z.string().optional(),
|
cashier_url: stringOrNull,
|
||||||
checkoutUrl: z.string().optional(),
|
checkoutUrl: stringOrNull,
|
||||||
checkout_url: z.string().optional(),
|
checkout_url: stringOrNull,
|
||||||
paymentUrl: z.string().optional(),
|
paymentUrl: stringOrNull,
|
||||||
payment_url: z.string().optional(),
|
payment_url: stringOrNull,
|
||||||
approvalUrl: z.string().optional(),
|
approvalUrl: stringOrNull,
|
||||||
approval_url: z.string().optional(),
|
approval_url: stringOrNull,
|
||||||
redirectUrl: z.string().optional(),
|
redirectUrl: stringOrNull,
|
||||||
redirect_url: z.string().optional(),
|
redirect_url: stringOrNull,
|
||||||
url: z.string().optional(),
|
url: stringOrNull,
|
||||||
}).transform((data) => {
|
}).transform((data) => {
|
||||||
const payParams = { ...data.payParams };
|
const payParams = { ...data.payParams };
|
||||||
|
|
||||||
|
|||||||
@@ -3,20 +3,26 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import {
|
||||||
|
booleanOrFalse,
|
||||||
|
numberOrNull,
|
||||||
|
stringOrNull,
|
||||||
|
} from "../nullable-defaults";
|
||||||
|
|
||||||
export const PaymentPlanSchema = z.object({
|
export const PaymentPlanSchema = z.object({
|
||||||
planId: z.string(),
|
planId: z.string(),
|
||||||
planName: z.string(),
|
planName: z.string(),
|
||||||
orderType: z.string(),
|
orderType: z.string(),
|
||||||
vipDays: z.number().nullable(),
|
vipDays: numberOrNull,
|
||||||
dolAmount: z.number().nullable(),
|
dolAmount: numberOrNull,
|
||||||
creditBalance: z.number(),
|
creditBalance: z.number(),
|
||||||
amountCents: z.number(),
|
amountCents: z.number(),
|
||||||
originalAmountCents: z.number().nullable().default(null),
|
originalAmountCents: numberOrNull,
|
||||||
dailyPriceCents: z.number().nullable().default(null),
|
dailyPriceCents: numberOrNull,
|
||||||
currency: z.string(),
|
currency: z.string(),
|
||||||
isFirstRechargeOffer: z.boolean().default(false),
|
isFirstRechargeOffer: booleanOrFalse,
|
||||||
firstRechargeDiscountPercent: z.number().nullable().default(null),
|
firstRechargeDiscountPercent: numberOrNull,
|
||||||
promotionType: z.string().nullable().default(null),
|
promotionType: stringOrNull,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type PaymentPlanInput = z.input<typeof PaymentPlanSchema>;
|
export type PaymentPlanInput = z.input<typeof PaymentPlanSchema>;
|
||||||
|
|||||||
@@ -3,7 +3,13 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { booleanOrFalse, numberOrZero, stringOrEmpty } from "../nullable-defaults";
|
import {
|
||||||
|
arrayOrEmpty,
|
||||||
|
booleanOrFalse,
|
||||||
|
numberOrZero,
|
||||||
|
schemaOrNull,
|
||||||
|
stringOrEmpty,
|
||||||
|
} from "../nullable-defaults";
|
||||||
import { PaymentPlanSchema } from "./payment_plan";
|
import { PaymentPlanSchema } from "./payment_plan";
|
||||||
|
|
||||||
export const FirstRechargeOfferSchema = z.object({
|
export const FirstRechargeOfferSchema = z.object({
|
||||||
@@ -13,9 +19,9 @@ export const FirstRechargeOfferSchema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const PaymentPlansResponseSchema = z.object({
|
export const PaymentPlansResponseSchema = z.object({
|
||||||
isFirstRecharge: z.boolean().default(false),
|
isFirstRecharge: booleanOrFalse,
|
||||||
firstRechargeOffer: FirstRechargeOfferSchema.nullable().default(null),
|
firstRechargeOffer: schemaOrNull(FirstRechargeOfferSchema),
|
||||||
plans: z.array(PaymentPlanSchema),
|
plans: arrayOrEmpty(PaymentPlanSchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type PaymentPlansResponseInput = z.input<
|
export type PaymentPlansResponseInput = z.input<
|
||||||
|
|||||||
@@ -3,9 +3,11 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { booleanOrFalse, stringOrNull } from "../nullable-defaults";
|
||||||
|
|
||||||
export const PaymentVipStatusResponseSchema = z.object({
|
export const PaymentVipStatusResponseSchema = z.object({
|
||||||
isVip: z.boolean(),
|
isVip: booleanOrFalse,
|
||||||
vipExpiresAt: z.string().nullable(),
|
vipExpiresAt: stringOrNull,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type PaymentVipStatusResponseInput = z.input<
|
export type PaymentVipStatusResponseInput = z.input<
|
||||||
|
|||||||
@@ -4,9 +4,11 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { stringOrEmpty } from "../nullable-defaults";
|
||||||
|
|
||||||
export const AvatarDataSchema = z.object({
|
export const AvatarDataSchema = z.object({
|
||||||
avatarUrl: z.string().default(""),
|
avatarUrl: stringOrEmpty,
|
||||||
userId: z.string(),
|
userId: stringOrEmpty,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type AvatarDataInput = z.input<typeof AvatarDataSchema>;
|
export type AvatarDataInput = z.input<typeof AvatarDataSchema>;
|
||||||
|
|||||||
@@ -4,9 +4,11 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { numberOrZero, stringOrEmpty } from "../nullable-defaults";
|
||||||
|
|
||||||
export const CreditsDataSchema = z.object({
|
export const CreditsDataSchema = z.object({
|
||||||
userId: z.string(),
|
userId: stringOrEmpty,
|
||||||
dolBalance: z.number(),
|
dolBalance: numberOrZero,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type CreditsDataInput = z.input<typeof CreditsDataSchema>;
|
export type CreditsDataInput = z.input<typeof CreditsDataSchema>;
|
||||||
|
|||||||
@@ -4,11 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { arrayOrEmpty, numberOrZero } from "../nullable-defaults";
|
||||||
|
|
||||||
export const CreditsHistoryDataSchema = z.object({
|
export const CreditsHistoryDataSchema = z.object({
|
||||||
records: z.array(z.record(z.string(), z.unknown())),
|
records: arrayOrEmpty(z.record(z.string(), z.unknown())),
|
||||||
total: z.number(),
|
total: numberOrZero,
|
||||||
limit: z.number(),
|
limit: numberOrZero,
|
||||||
offset: z.number(),
|
offset: numberOrZero,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type CreditsHistoryDataInput = z.input<typeof CreditsHistoryDataSchema>;
|
export type CreditsHistoryDataInput = z.input<typeof CreditsHistoryDataSchema>;
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { numberOr } from "../nullable-defaults";
|
||||||
|
|
||||||
export const PERSONALITY_TRAITS_DEFAULTS = {
|
export const PERSONALITY_TRAITS_DEFAULTS = {
|
||||||
cheerful: 0.5,
|
cheerful: 0.5,
|
||||||
caring: 0.5,
|
caring: 0.5,
|
||||||
@@ -13,11 +15,11 @@ export const PERSONALITY_TRAITS_DEFAULTS = {
|
|||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const PersonalityTraitsSchema = z.object({
|
export const PersonalityTraitsSchema = z.object({
|
||||||
cheerful: z.number().default(0.5),
|
cheerful: numberOr(0.5),
|
||||||
caring: z.number().default(0.5),
|
caring: numberOr(0.5),
|
||||||
playful: z.number().default(0.5),
|
playful: numberOr(0.5),
|
||||||
serious: z.number().default(0.5),
|
serious: numberOr(0.5),
|
||||||
romantic: z.number().default(0.5),
|
romantic: numberOr(0.5),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type PersonalityTraitsInput = z.input<typeof PersonalityTraitsSchema>;
|
export type PersonalityTraitsInput = z.input<typeof PersonalityTraitsSchema>;
|
||||||
|
|||||||
@@ -4,11 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { numberOrZero, stringOrEmpty } from "../nullable-defaults";
|
||||||
|
|
||||||
export const RecentMemorySchema = z.object({
|
export const RecentMemorySchema = z.object({
|
||||||
type: z.string(),
|
type: stringOrEmpty,
|
||||||
content: z.string(),
|
content: stringOrEmpty,
|
||||||
importance: z.number(),
|
importance: numberOrZero,
|
||||||
createdAt: z.string().default(""),
|
createdAt: stringOrEmpty,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type RecentMemoryInput = z.input<typeof RecentMemorySchema>;
|
export type RecentMemoryInput = z.input<typeof RecentMemorySchema>;
|
||||||
|
|||||||
@@ -4,11 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { stringOrEmpty } from "../nullable-defaults";
|
||||||
|
|
||||||
export const UpdateProfileRequestSchema = z.object({
|
export const UpdateProfileRequestSchema = z.object({
|
||||||
username: z.string().default(""),
|
username: stringOrEmpty,
|
||||||
nickname: z.string().default(""),
|
nickname: stringOrEmpty,
|
||||||
preferredLanguage: z.string().default(""),
|
preferredLanguage: stringOrEmpty,
|
||||||
currentMood: z.string().default(""),
|
currentMood: stringOrEmpty,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type UpdateProfileRequestInput = z.input<typeof UpdateProfileRequestSchema>;
|
export type UpdateProfileRequestInput = z.input<typeof UpdateProfileRequestSchema>;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { z } from "zod";
|
|||||||
import { PersonalityTraitsSchema, PERSONALITY_TRAITS_DEFAULTS } from "./personality_traits";
|
import { PersonalityTraitsSchema, PERSONALITY_TRAITS_DEFAULTS } from "./personality_traits";
|
||||||
import {
|
import {
|
||||||
numberOrZero,
|
numberOrZero,
|
||||||
|
schemaOr,
|
||||||
stringOrEmpty,
|
stringOrEmpty,
|
||||||
} from "../nullable-defaults";
|
} from "../nullable-defaults";
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ export const UserSchema = z.object({
|
|||||||
dailyFreePrivateLimit: numberOrZero,
|
dailyFreePrivateLimit: numberOrZero,
|
||||||
dailyFreePrivateUsed: numberOrZero,
|
dailyFreePrivateUsed: numberOrZero,
|
||||||
dailyFreePrivateRemaining: numberOrZero,
|
dailyFreePrivateRemaining: numberOrZero,
|
||||||
personalityTraits: PersonalityTraitsSchema.default(PERSONALITY_TRAITS_DEFAULTS),
|
personalityTraits: schemaOr(PersonalityTraitsSchema, PERSONALITY_TRAITS_DEFAULTS),
|
||||||
preferredLanguage: stringOrEmpty,
|
preferredLanguage: stringOrEmpty,
|
||||||
createdAt: stringOrEmpty,
|
createdAt: stringOrEmpty,
|
||||||
// 后端新游客返回 null("还没发过消息"),前端统一收敛为空字符串。
|
// 后端新游客返回 null("还没发过消息"),前端统一收敛为空字符串。
|
||||||
|
|||||||
@@ -6,9 +6,49 @@ import { z } from "zod";
|
|||||||
import {
|
import {
|
||||||
booleanOrFalse,
|
booleanOrFalse,
|
||||||
numberOrZero,
|
numberOrZero,
|
||||||
|
schemaOr,
|
||||||
stringOrEmpty,
|
stringOrEmpty,
|
||||||
|
stringOrNull,
|
||||||
} from "../nullable-defaults";
|
} from "../nullable-defaults";
|
||||||
|
|
||||||
|
const POLICY_DEFAULTS = {
|
||||||
|
membershipState: "",
|
||||||
|
nonVipEntitlementsShared: false,
|
||||||
|
guestSameAsLoggedInNonVip: false,
|
||||||
|
refreshAfterPayment: "",
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const COSTS_DEFAULTS = {
|
||||||
|
normal_message: 0,
|
||||||
|
private_message: 0,
|
||||||
|
voice_message: 0,
|
||||||
|
photo: 0,
|
||||||
|
voice_call_minute: 0,
|
||||||
|
private_album_10: 0,
|
||||||
|
private_album_20: 0,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const QUOTAS_DEFAULTS = {
|
||||||
|
normalChatFreeDaily: 0,
|
||||||
|
privateUnlockFreeDaily: 0,
|
||||||
|
voiceMessageFreeDaily: 0,
|
||||||
|
photoFreeDaily: 0,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const HISTORY_UNLOCK_COSTS_DEFAULTS = {
|
||||||
|
private_message: 0,
|
||||||
|
voice_message: 0,
|
||||||
|
photo: 0,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const HISTORY_UNLOCK_DEFAULTS = {
|
||||||
|
enabled: false,
|
||||||
|
order: "",
|
||||||
|
chargeMode: "",
|
||||||
|
insufficientBalanceBehavior: "",
|
||||||
|
costs: HISTORY_UNLOCK_COSTS_DEFAULTS,
|
||||||
|
} as const;
|
||||||
|
|
||||||
export const UserEntitlementsPolicySchema = z
|
export const UserEntitlementsPolicySchema = z
|
||||||
.object({
|
.object({
|
||||||
membershipState: stringOrEmpty,
|
membershipState: stringOrEmpty,
|
||||||
@@ -39,24 +79,24 @@ export const UserEntitlementsQuotasSchema = z
|
|||||||
})
|
})
|
||||||
.passthrough();
|
.passthrough();
|
||||||
|
|
||||||
|
export const UserEntitlementsHistoryUnlockCostsSchema = z
|
||||||
|
.object({
|
||||||
|
private_message: numberOrZero,
|
||||||
|
voice_message: numberOrZero,
|
||||||
|
photo: numberOrZero,
|
||||||
|
})
|
||||||
|
.passthrough();
|
||||||
|
|
||||||
export const UserEntitlementsHistoryUnlockSchema = z
|
export const UserEntitlementsHistoryUnlockSchema = z
|
||||||
.object({
|
.object({
|
||||||
enabled: booleanOrFalse,
|
enabled: booleanOrFalse,
|
||||||
order: stringOrEmpty,
|
order: stringOrEmpty,
|
||||||
chargeMode: stringOrEmpty,
|
chargeMode: stringOrEmpty,
|
||||||
insufficientBalanceBehavior: stringOrEmpty,
|
insufficientBalanceBehavior: stringOrEmpty,
|
||||||
costs: z
|
costs: schemaOr(
|
||||||
.object({
|
UserEntitlementsHistoryUnlockCostsSchema,
|
||||||
private_message: numberOrZero,
|
HISTORY_UNLOCK_COSTS_DEFAULTS,
|
||||||
voice_message: numberOrZero,
|
),
|
||||||
photo: numberOrZero,
|
|
||||||
})
|
|
||||||
.passthrough()
|
|
||||||
.default(() => ({
|
|
||||||
private_message: 0,
|
|
||||||
voice_message: 0,
|
|
||||||
photo: 0,
|
|
||||||
})),
|
|
||||||
})
|
})
|
||||||
.passthrough();
|
.passthrough();
|
||||||
|
|
||||||
@@ -64,41 +104,16 @@ export const UserEntitlementsSchema = z.object({
|
|||||||
userId: stringOrEmpty,
|
userId: stringOrEmpty,
|
||||||
isGuest: booleanOrFalse,
|
isGuest: booleanOrFalse,
|
||||||
isVip: booleanOrFalse,
|
isVip: booleanOrFalse,
|
||||||
vipExpiresAt: z.string().nullable().default(null),
|
vipExpiresAt: stringOrNull,
|
||||||
creditBalance: numberOrZero,
|
creditBalance: numberOrZero,
|
||||||
dolBalance: numberOrZero,
|
dolBalance: numberOrZero,
|
||||||
policy: UserEntitlementsPolicySchema.default(() => ({
|
policy: schemaOr(UserEntitlementsPolicySchema, POLICY_DEFAULTS),
|
||||||
membershipState: "",
|
costs: schemaOr(UserEntitlementsCostsSchema, COSTS_DEFAULTS),
|
||||||
nonVipEntitlementsShared: false,
|
quotas: schemaOr(UserEntitlementsQuotasSchema, QUOTAS_DEFAULTS),
|
||||||
guestSameAsLoggedInNonVip: false,
|
historyUnlock: schemaOr(
|
||||||
refreshAfterPayment: "",
|
UserEntitlementsHistoryUnlockSchema,
|
||||||
})),
|
HISTORY_UNLOCK_DEFAULTS,
|
||||||
costs: UserEntitlementsCostsSchema.default(() => ({
|
),
|
||||||
normal_message: 0,
|
|
||||||
private_message: 0,
|
|
||||||
voice_message: 0,
|
|
||||||
photo: 0,
|
|
||||||
voice_call_minute: 0,
|
|
||||||
private_album_10: 0,
|
|
||||||
private_album_20: 0,
|
|
||||||
})),
|
|
||||||
quotas: UserEntitlementsQuotasSchema.default(() => ({
|
|
||||||
normalChatFreeDaily: 0,
|
|
||||||
privateUnlockFreeDaily: 0,
|
|
||||||
voiceMessageFreeDaily: 0,
|
|
||||||
photoFreeDaily: 0,
|
|
||||||
})),
|
|
||||||
historyUnlock: UserEntitlementsHistoryUnlockSchema.default(() => ({
|
|
||||||
enabled: false,
|
|
||||||
order: "",
|
|
||||||
chargeMode: "",
|
|
||||||
insufficientBalanceBehavior: "",
|
|
||||||
costs: {
|
|
||||||
private_message: 0,
|
|
||||||
voice_message: 0,
|
|
||||||
photo: 0,
|
|
||||||
},
|
|
||||||
})),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type UserEntitlementsInput = z.input<typeof UserEntitlementsSchema>;
|
export type UserEntitlementsInput = z.input<typeof UserEntitlementsSchema>;
|
||||||
|
|||||||
@@ -3,26 +3,33 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import {
|
||||||
|
arrayOrEmpty,
|
||||||
|
numberOrZero,
|
||||||
|
schemaOr,
|
||||||
|
stringOrEmpty,
|
||||||
|
} from "../nullable-defaults";
|
||||||
import { PersonalityTraitsSchema, PERSONALITY_TRAITS_DEFAULTS } from "./personality_traits";
|
import { PersonalityTraitsSchema, PERSONALITY_TRAITS_DEFAULTS } from "./personality_traits";
|
||||||
import { RecentMemorySchema } from "./recent_memory";
|
import { RecentMemorySchema } from "./recent_memory";
|
||||||
|
|
||||||
export const UserStatsResponseSchema = z.object({
|
export const UserStatsResponseSchema = z.object({
|
||||||
userId: z.string(),
|
userId: stringOrEmpty,
|
||||||
username: z.string(),
|
username: stringOrEmpty,
|
||||||
platform: z.string(),
|
platform: stringOrEmpty,
|
||||||
intimacy: z.number(),
|
intimacy: numberOrZero,
|
||||||
relationshipStage: z.string(),
|
relationshipStage: stringOrEmpty,
|
||||||
dolBalance: z.number(),
|
dolBalance: numberOrZero,
|
||||||
totalMessages: z.number(),
|
totalMessages: numberOrZero,
|
||||||
lastMessageAt: z.string().default(""),
|
lastMessageAt: stringOrEmpty,
|
||||||
accountCreatedAt: z.string().default(""),
|
accountCreatedAt: stringOrEmpty,
|
||||||
currentMood: z.string().default(""),
|
currentMood: stringOrEmpty,
|
||||||
personalityTraits: PersonalityTraitsSchema.default(PERSONALITY_TRAITS_DEFAULTS),
|
personalityTraits: schemaOr(PersonalityTraitsSchema, PERSONALITY_TRAITS_DEFAULTS),
|
||||||
recentMemories: z.array(RecentMemorySchema).default([]),
|
recentMemories: arrayOrEmpty(RecentMemorySchema),
|
||||||
promptTokensTotal: z.number().default(0),
|
promptTokensTotal: numberOrZero,
|
||||||
completionTokensTotal: z.number().default(0),
|
completionTokensTotal: numberOrZero,
|
||||||
cacheHitTokensTotal: z.number().default(0),
|
cacheHitTokensTotal: numberOrZero,
|
||||||
tokensTotal: z.number().default(0),
|
tokensTotal: numberOrZero,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type UserStatsResponseInput = z.input<typeof UserStatsResponseSchema>;
|
export type UserStatsResponseInput = z.input<typeof UserStatsResponseSchema>;
|
||||||
|
|||||||
Reference in New Issue
Block a user