refactor(data): split models into separate dto and schema modules

Reorganize the data layer by separating DTOs (Data Transfer Objects) from
Zod validation schemas into distinct directory structures. Previously,
all model types were imported from a single `@/data/models` barrel
file, mixing request/response classes with validation schemas.

Changes:
- Update API files (auth, chat, metrics, user) to import DTOs from
  specific paths under `@/data/dto/*` instead of the models barrel
- Move user schema to `@/data/schemas/user/user` for type-only imports
- Create dedicated Zod schema files under `@/data/schemas/*` for
  auth and chat domain models (e.g., AppleLoginRequestSchema,
  SendMessageRequestSchema, SyncMessageSchema, SttDataSchema)
- Improve discoverability and tree-shaking by co-locating schemas
  with their respective domain areas

This is a non-functional refactor that lays the groundwork for
clearer separation between transport-layer types and runtime
validation.
This commit is contained in:
2026-06-08 19:14:40 +08:00
parent 67a72783fe
commit 75e685d418
89 changed files with 1119 additions and 853 deletions
+31
View File
@@ -0,0 +1,31 @@
/**
* Apple 登录请求 DTO
*/
import {
AppleLoginRequestSchema,
type AppleLoginRequestInput,
type AppleLoginRequestData,
} from "@/data/schemas/auth/apple_login_request";
export class AppleLoginRequest {
declare readonly identityToken: string;
declare readonly platform: string;
private constructor(input: AppleLoginRequestInput) {
const data = AppleLoginRequestSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: AppleLoginRequestInput): AppleLoginRequest {
return new AppleLoginRequest(input);
}
static fromJson(json: unknown): AppleLoginRequest {
return AppleLoginRequest.from(json as AppleLoginRequestInput);
}
toJson(): AppleLoginRequestData {
return AppleLoginRequestSchema.parse(this);
}
}
@@ -0,0 +1,32 @@
/**
* Facebook 登录请求 DTO
*/
import {
FacebookLoginRequestSchema,
type FacebookLoginRequestInput,
type FacebookLoginRequestData,
} from "@/data/schemas/auth/facebook_login_request";
export class FacebookLoginRequest {
declare readonly accessToken: string;
declare readonly platform: string;
declare readonly guestId: string;
private constructor(input: FacebookLoginRequestInput) {
const data = FacebookLoginRequestSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: FacebookLoginRequestInput): FacebookLoginRequest {
return new FacebookLoginRequest(input);
}
static fromJson(json: unknown): FacebookLoginRequest {
return FacebookLoginRequest.from(json as FacebookLoginRequestInput);
}
toJson(): FacebookLoginRequestData {
return FacebookLoginRequestSchema.parse(this);
}
}
+31
View File
@@ -0,0 +1,31 @@
/**
* Facebook ID 登录请求 DTO
*/
import {
FbIdLoginRequestSchema,
type FbIdLoginRequestInput,
type FbIdLoginRequestData,
} from "@/data/schemas/auth/fb_id_login_request";
export class FbIdLoginRequest {
declare readonly fbId: string;
declare readonly avatarUrl: string;
private constructor(input: FbIdLoginRequestInput) {
const data = FbIdLoginRequestSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: FbIdLoginRequestInput): FbIdLoginRequest {
return new FbIdLoginRequest(input);
}
static fromJson(json: unknown): FbIdLoginRequest {
return FbIdLoginRequest.from(json as FbIdLoginRequestInput);
}
toJson(): FbIdLoginRequestData {
return FbIdLoginRequestSchema.parse(this);
}
}
+32
View File
@@ -0,0 +1,32 @@
/**
* Google 登录请求 DTO
*/
import {
GoogleLoginRequestSchema,
type GoogleLoginRequestInput,
type GoogleLoginRequestData,
} from "@/data/schemas/auth/google_login_request";
export class GoogleLoginRequest {
declare readonly idToken: string;
declare readonly platform: string;
declare readonly guestId: string;
private constructor(input: GoogleLoginRequestInput) {
const data = GoogleLoginRequestSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: GoogleLoginRequestInput): GoogleLoginRequest {
return new GoogleLoginRequest(input);
}
static fromJson(json: unknown): GoogleLoginRequest {
return GoogleLoginRequest.from(json as GoogleLoginRequestInput);
}
toJson(): GoogleLoginRequestData {
return GoogleLoginRequestSchema.parse(this);
}
}
+30
View File
@@ -0,0 +1,30 @@
/**
* 游客登录请求 DTO
*/
import {
GuestLoginRequestSchema,
type GuestLoginRequestInput,
type GuestLoginRequestData,
} from "@/data/schemas/auth/guest_login_request";
export class GuestLoginRequest {
declare readonly deviceId: string;
private constructor(input: GuestLoginRequestInput) {
const data = GuestLoginRequestSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: GuestLoginRequestInput): GuestLoginRequest {
return new GuestLoginRequest(input);
}
static fromJson(json: unknown): GuestLoginRequest {
return GuestLoginRequest.from(json as GuestLoginRequestInput);
}
toJson(): GuestLoginRequestData {
return GuestLoginRequestSchema.parse(this);
}
}
+40
View File
@@ -0,0 +1,40 @@
/**
* 游客登录响应 DTO
*/
import {
GuestLoginResponseSchema,
type GuestLoginResponseInput,
type GuestLoginResponseData,
} from "@/data/schemas/auth/guest_login_response";
import { User } from "@/data/dto/user/user";
export class GuestLoginResponse {
declare readonly token: string;
declare readonly deviceId: string;
declare readonly userId: string;
declare readonly isDeviceUser: boolean;
declare readonly expiresIn: number;
declare readonly user: User;
private constructor(input: GuestLoginResponseInput) {
const parsed = GuestLoginResponseSchema.parse(input);
Object.assign(this, {
...parsed,
user: parsed.user ? User.fromJson(parsed.user) : User.empty(),
});
Object.freeze(this);
}
static from(input: GuestLoginResponseInput): GuestLoginResponse {
return new GuestLoginResponse(input);
}
static fromJson(json: unknown): GuestLoginResponse {
return GuestLoginResponse.from(json as GuestLoginResponseInput);
}
toJson(): GuestLoginResponseData {
const data = GuestLoginResponseSchema.parse(this);
return data;
}
}
+34
View File
@@ -0,0 +1,34 @@
/**
* 登录请求 DTO
*/
import {
LoginRequestSchema,
type LoginRequestInput,
type LoginRequestData,
} from "@/data/schemas/auth/login_request";
export class LoginRequest {
declare readonly email: string;
declare readonly username: string;
declare readonly password: string;
declare readonly platform: string;
declare readonly guestId: string;
private constructor(input: LoginRequestInput) {
const data = LoginRequestSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: LoginRequestInput): LoginRequest {
return new LoginRequest(input);
}
static fromJson(json: unknown): LoginRequest {
return LoginRequest.from(json as LoginRequestInput);
}
toJson(): LoginRequestData {
return LoginRequestSchema.parse(this);
}
}
+36
View File
@@ -0,0 +1,36 @@
/**
* 登录响应 DTO
*/
import {
LoginResponseSchema,
type LoginResponseInput,
type LoginResponseData,
} from "@/data/schemas/auth/login_response";
import { User } from "@/data/dto/user/user";
export class LoginResponse {
declare readonly user: User;
declare readonly token: string;
declare readonly refreshToken: string;
private constructor(input: LoginResponseInput) {
const data = LoginResponseSchema.parse(input);
Object.assign(this, {
...data,
user: User.fromJson(data.user),
});
Object.freeze(this);
}
static from(input: LoginResponseInput): LoginResponse {
return new LoginResponse(input);
}
static fromJson(json: unknown): LoginResponse {
return LoginResponse.from(json as LoginResponseInput);
}
toJson(): LoginResponseData {
return LoginResponseSchema.parse(this);
}
}
+30
View File
@@ -0,0 +1,30 @@
/**
* 退出登录响应 DTO
*/
import {
LogoutResponseSchema,
type LogoutResponseInput,
type LogoutResponseData,
} from "@/data/schemas/auth/logout_response";
export class LogoutResponse {
declare readonly success: boolean;
private constructor(input: LogoutResponseInput) {
const data = LogoutResponseSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: LogoutResponseInput): LogoutResponse {
return new LogoutResponse(input);
}
static fromJson(json: unknown): LogoutResponse {
return LogoutResponse.from(json as LogoutResponseInput);
}
toJson(): LogoutResponseData {
return LogoutResponseSchema.parse(this);
}
}
@@ -0,0 +1,30 @@
/**
* 刷新 Token 请求 DTO
*/
import {
RefreshTokenRequestSchema,
type RefreshTokenRequestInput,
type RefreshTokenRequestData,
} from "@/data/schemas/auth/refresh_token_request";
export class RefreshTokenRequest {
declare readonly refreshToken: string;
private constructor(input: RefreshTokenRequestInput) {
const data = RefreshTokenRequestSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: RefreshTokenRequestInput): RefreshTokenRequest {
return new RefreshTokenRequest(input);
}
static fromJson(json: unknown): RefreshTokenRequest {
return RefreshTokenRequest.from(json as RefreshTokenRequestInput);
}
toJson(): RefreshTokenRequestData {
return RefreshTokenRequestSchema.parse(this);
}
}
@@ -0,0 +1,32 @@
/**
* 刷新 Token 响应 DTO
*/
import {
RefreshTokenResponseSchema,
type RefreshTokenResponseInput,
type RefreshTokenResponseData,
} from "@/data/schemas/auth/refresh_token_response";
export class RefreshTokenResponse {
declare readonly token: string;
declare readonly refreshToken: string;
declare readonly userId: string;
private constructor(input: RefreshTokenResponseInput) {
const data = RefreshTokenResponseSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: RefreshTokenResponseInput): RefreshTokenResponse {
return new RefreshTokenResponse(input);
}
static fromJson(json: unknown): RefreshTokenResponse {
return RefreshTokenResponse.from(json as RefreshTokenResponseInput);
}
toJson(): RefreshTokenResponseData {
return RefreshTokenResponseSchema.parse(this);
}
}
+34
View File
@@ -0,0 +1,34 @@
/**
* 注册请求 DTO
*/
import {
RegisterRequestSchema,
type RegisterRequestInput,
type RegisterRequestData,
} from "@/data/schemas/auth/register_request";
export class RegisterRequest {
declare readonly username: string;
declare readonly email: string;
declare readonly password: string;
declare readonly platform: string;
declare readonly guestId: string;
private constructor(input: RegisterRequestInput) {
const data = RegisterRequestSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: RegisterRequestInput): RegisterRequest {
return new RegisterRequest(input);
}
static fromJson(json: unknown): RegisterRequest {
return RegisterRequest.from(json as RegisterRequestInput);
}
toJson(): RegisterRequestData {
return RegisterRequestSchema.parse(this);
}
}
+30
View File
@@ -0,0 +1,30 @@
/**
* 发送验证码请求 DTO
*/
import {
SendCodeRequestSchema,
type SendCodeRequestInput,
type SendCodeRequestData,
} from "@/data/schemas/auth/send_code_request";
export class SendCodeRequest {
declare readonly email: string;
private constructor(input: SendCodeRequestInput) {
const data = SendCodeRequestSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: SendCodeRequestInput): SendCodeRequest {
return new SendCodeRequest(input);
}
static fromJson(json: unknown): SendCodeRequest {
return SendCodeRequest.from(json as SendCodeRequestInput);
}
toJson(): SendCodeRequestData {
return SendCodeRequestSchema.parse(this);
}
}
@@ -0,0 +1,37 @@
/**
* 聊天历史响应 DTO
*/
import {
ChatHistoryResponseSchema,
type ChatHistoryResponseInput,
type ChatHistoryResponseData,
} from "@/data/schemas/chat/chat_history_response";
import { ChatMessage } from "./chat_message";
export class ChatHistoryResponse {
declare readonly messages: ChatMessage[];
declare readonly total: number;
declare readonly limit: number;
declare readonly offset: number;
private constructor(input: ChatHistoryResponseInput) {
const data = ChatHistoryResponseSchema.parse(input);
Object.assign(this, {
...data,
messages: data.messages.map((m) => ChatMessage.from(m)),
});
Object.freeze(this);
}
static from(input: ChatHistoryResponseInput): ChatHistoryResponse {
return new ChatHistoryResponse(input);
}
static fromJson(json: unknown): ChatHistoryResponse {
return ChatHistoryResponse.from(json as ChatHistoryResponseInput);
}
toJson(): ChatHistoryResponseData {
return ChatHistoryResponseSchema.parse(this);
}
}
+33
View File
@@ -0,0 +1,33 @@
/**
* 聊天历史中的单条消息 DTO
*/
import {
ChatMessageSchema,
type ChatMessageInput,
type ChatMessageData,
} from "@/data/schemas/chat/chat_message";
export class ChatMessage {
declare readonly role: string;
declare readonly content: string;
declare readonly id: string;
declare readonly createdAt: string;
private constructor(input: ChatMessageInput) {
const data = ChatMessageSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: ChatMessageInput): ChatMessage {
return new ChatMessage(input);
}
static fromJson(json: unknown): ChatMessage {
return ChatMessage.from(json as ChatMessageInput);
}
toJson(): ChatMessageData {
return ChatMessageSchema.parse(this);
}
}
+40
View File
@@ -0,0 +1,40 @@
/**
* 发送消息响应 DTO
*/
import {
ChatSendResponseSchema,
type ChatSendResponseInput,
type ChatSendResponseData,
} from "@/data/schemas/chat/chat_send_response";
export class ChatSendResponse {
declare readonly mode: string;
declare readonly reply: string;
declare readonly voiceUrl: string;
declare readonly audioUrl: string;
declare readonly intimidadChange: number;
declare readonly newIntimacy: number;
declare readonly relationshipStage: string;
declare readonly currentMood: string;
declare readonly messageId: string;
declare readonly isGuest: boolean;
declare readonly timestamp: number;
private constructor(input: ChatSendResponseInput) {
const data = ChatSendResponseSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: ChatSendResponseInput): ChatSendResponse {
return new ChatSendResponse(input);
}
static fromJson(json: unknown): ChatSendResponse {
return ChatSendResponse.from(json as ChatSendResponseInput);
}
toJson(): ChatSendResponseData {
return ChatSendResponseSchema.parse(this);
}
}
+31
View File
@@ -0,0 +1,31 @@
/**
* 消息同步响应 DTO
*/
import {
ChatSyncDataSchema,
type ChatSyncDataInput,
type ChatSyncDataData,
} from "@/data/schemas/chat/chat_sync_data";
export class ChatSyncData {
declare readonly syncedCount: number;
declare readonly totalHistory: number;
private constructor(input: ChatSyncDataInput) {
const data = ChatSyncDataSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: ChatSyncDataInput): ChatSyncData {
return new ChatSyncData(input);
}
static fromJson(json: unknown): ChatSyncData {
return ChatSyncData.from(json as ChatSyncDataInput);
}
toJson(): ChatSyncDataData {
return ChatSyncDataSchema.parse(this);
}
}
+34
View File
@@ -0,0 +1,34 @@
/**
* 消息同步请求 DTO
*/
import {
ChatSyncRequestSchema,
type ChatSyncRequestInput,
type ChatSyncRequestData,
} from "@/data/schemas/chat/chat_sync_request";
import { SyncMessage } from "./sync_message";
export class ChatSyncRequest {
declare readonly messages: SyncMessage[];
private constructor(input: ChatSyncRequestInput) {
const data = ChatSyncRequestSchema.parse(input);
Object.assign(this, {
...data,
messages: data.messages.map((m) => SyncMessage.from(m)),
});
Object.freeze(this);
}
static from(input: ChatSyncRequestInput): ChatSyncRequest {
return new ChatSyncRequest(input);
}
static fromJson(json: unknown): ChatSyncRequest {
return ChatSyncRequest.from(json as ChatSyncRequestInput);
}
toJson(): ChatSyncRequestData {
return ChatSyncRequestSchema.parse(this);
}
}
+98
View File
@@ -0,0 +1,98 @@
/**
* 游客每日聊天配额数据 DTO
*/
import {
GuestChatQuotaSchema,
type GuestChatQuotaInput,
type GuestChatQuotaData,
} from "@/data/schemas/chat/guest_chat_quota";
export class GuestChatQuota {
// 静态常量
static readonly maxQuotaPerDay = 40;
static readonly maxQuotaPerDayTest = 4;
static readonly quotaWarningThreshold = 20;
static readonly quotaWarningThresholdTest = 2;
static readonly defaultTotalQuota = 50;
static readonly defaultTotalQuotaDev = 5;
static readonly quotaExhausted = 0;
declare readonly remaining: number;
declare readonly date: string;
private constructor(input: GuestChatQuotaInput) {
const data = GuestChatQuotaSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: GuestChatQuotaInput): GuestChatQuota {
return new GuestChatQuota(input);
}
static fromJson(json: unknown): GuestChatQuota {
return GuestChatQuota.from(json as GuestChatQuotaInput);
}
/**
* 配额耗尽状态值
*/
static get exhausted(): number {
return GuestChatQuota.quotaExhausted;
}
/**
* 是否处于开发环境
*/
static get isDevelopment(): boolean {
return process.env.NODE_ENV !== "production";
}
/**
* 获取当前环境的配额警告阈值
*/
static get warningThreshold(): number {
return GuestChatQuota.isDevelopment
? GuestChatQuota.quotaWarningThresholdTest
: GuestChatQuota.quotaWarningThreshold;
}
/**
* 获取当前环境的每日最大配额
*/
static get threshold(): number {
return GuestChatQuota.isDevelopment
? GuestChatQuota.maxQuotaPerDayTest
: GuestChatQuota.maxQuotaPerDay;
}
/**
* 获取当前环境的总配额默认值
*/
static get totalQuotaDefault(): number {
return GuestChatQuota.isDevelopment
? GuestChatQuota.defaultTotalQuotaDev
: GuestChatQuota.defaultTotalQuota;
}
/**
* 创建初始配额实例
*/
static initial(todayString: string): GuestChatQuota {
return new GuestChatQuota({
remaining: GuestChatQuota.threshold,
date: todayString,
});
}
/**
* 是否需要重置(跨天)
*/
needsReset(todayString: string): boolean {
return this.date !== todayString;
}
toJson(): GuestChatQuotaData {
return GuestChatQuotaSchema.parse(this);
}
}
@@ -0,0 +1,37 @@
/**
* 图片上传响应 DTO
*/
import {
ImageUploadResponseSchema,
type ImageUploadResponseInput,
type ImageUploadResponseData,
} from "@/data/schemas/chat/image_upload_response";
export class ImageUploadResponse {
declare readonly success: boolean;
declare readonly imageId: string;
declare readonly thumbUrl: string;
declare readonly mediumUrl: string;
declare readonly originalUrl: string;
declare readonly width: number;
declare readonly height: number;
declare readonly bytes: number;
private constructor(input: ImageUploadResponseInput) {
const data = ImageUploadResponseSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: ImageUploadResponseInput): ImageUploadResponse {
return new ImageUploadResponse(input);
}
static fromJson(json: unknown): ImageUploadResponse {
return ImageUploadResponse.from(json as ImageUploadResponseInput);
}
toJson(): ImageUploadResponseData {
return ImageUploadResponseSchema.parse(this);
}
}
+38
View File
@@ -0,0 +1,38 @@
/**
* 发送消息请求 DTO
*/
import {
SendMessageRequestSchema,
type SendMessageRequestInput,
type SendMessageRequestData,
} from "@/data/schemas/chat/send_message_request";
export class SendMessageRequest {
declare readonly message: string;
declare readonly image: string;
declare readonly imageId: string;
declare readonly imageThumbUrl: string;
declare readonly imageMediumUrl: string;
declare readonly imageOriginalUrl: string;
declare readonly imageWidth: number;
declare readonly imageHeight: number;
declare readonly useWebSocket: boolean;
private constructor(input: SendMessageRequestInput) {
const data = SendMessageRequestSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: SendMessageRequestInput): SendMessageRequest {
return new SendMessageRequest(input);
}
static fromJson(json: unknown): SendMessageRequest {
return SendMessageRequest.from(json as SendMessageRequestInput);
}
toJson(): SendMessageRequestData {
return SendMessageRequestSchema.parse(this);
}
}
+30
View File
@@ -0,0 +1,30 @@
/**
* STT(语音转文字)响应 DTO
*/
import {
SttDataSchema,
type SttDataInput,
type SttDataData,
} from "@/data/schemas/chat/stt_data";
export class SttData {
declare readonly text: string;
private constructor(input: SttDataInput) {
const data = SttDataSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: SttDataInput): SttData {
return new SttData(input);
}
static fromJson(json: unknown): SttData {
return SttData.from(json as SttDataInput);
}
toJson(): SttDataData {
return SttDataSchema.parse(this);
}
}
+32
View File
@@ -0,0 +1,32 @@
/**
* 待同步的单条消息 DTO
*/
import {
SyncMessageSchema,
type SyncMessageInput,
type SyncMessageData,
} from "@/data/schemas/chat/sync_message";
export class SyncMessage {
declare readonly role: string;
declare readonly content: string;
declare readonly timestamp: string;
private constructor(input: SyncMessageInput) {
const data = SyncMessageSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: SyncMessageInput): SyncMessage {
return new SyncMessage(input);
}
static fromJson(json: unknown): SyncMessage {
return SyncMessage.from(json as SyncMessageInput);
}
toJson(): SyncMessageData {
return SyncMessageSchema.parse(this);
}
}
+42
View File
@@ -0,0 +1,42 @@
/**
* DTO - 集中导出
*
* 不可变运行时类(API 层使用)
* 原始 Dart: lib/data/models/
*/
export * from "./auth/apple_login_request";
export * from "./auth/facebook_login_request";
export * from "./auth/fb_id_login_request";
export * from "./auth/google_login_request";
export * from "./auth/guest_login_request";
export * from "./auth/guest_login_response";
export * from "./auth/login_request";
export * from "./auth/login_response";
export * from "./auth/logout_response";
export * from "./auth/refresh_token_request";
export * from "./auth/refresh_token_response";
export * from "./auth/register_request";
export * from "./auth/send_code_request";
export * from "./chat/chat_history_response";
export * from "./chat/chat_message";
export * from "./chat/chat_send_response";
export * from "./chat/chat_sync_data";
export * from "./chat/chat_sync_request";
export * from "./chat/guest_chat_quota";
export * from "./chat/image_upload_response";
export * from "./chat/send_message_request";
export * from "./chat/stt_data";
export * from "./chat/sync_message";
export * from "./metrics/app_event";
export * from "./metrics/pwa_event";
export * from "./user/avatar_data";
export * from "./user/credits_data";
export * from "./user/credits_history_data";
export * from "./user/personality_traits";
export * from "./user/recent_memory";
export * from "./user/update_profile_request";
export * from "./user/user";
export * from "./user/user_stats_response";
+32
View File
@@ -0,0 +1,32 @@
/**
* 应用事件上报请求 DTO
*/
import {
AppEventSchema,
type AppEventInput,
type AppEventData,
} from "@/data/schemas/metrics/app_event";
export class AppEvent {
declare readonly userId: string;
declare readonly browser: string;
declare readonly userAgent: string;
private constructor(input: AppEventInput) {
const data = AppEventSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: AppEventInput): AppEvent {
return new AppEvent(input);
}
static fromJson(json: unknown): AppEvent {
return AppEvent.from(json as AppEventInput);
}
toJson(): AppEventData {
return AppEventSchema.parse(this);
}
}
+34
View File
@@ -0,0 +1,34 @@
/**
* PWA 事件上报请求 DTO
*/
import {
PwaEventSchema,
type PwaEventInput,
type PwaEventData,
} from "@/data/schemas/metrics/pwa_event";
export class PwaEvent {
declare readonly deviceId: string;
declare readonly deviceType: string;
declare readonly timestamp: number;
declare readonly pwaInstalled: boolean;
declare readonly pwaSupported: boolean;
private constructor(input: PwaEventInput) {
const data = PwaEventSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: PwaEventInput): PwaEvent {
return new PwaEvent(input);
}
static fromJson(json: unknown): PwaEvent {
return PwaEvent.from(json as PwaEventInput);
}
toJson(): PwaEventData {
return PwaEventSchema.parse(this);
}
}
+31
View File
@@ -0,0 +1,31 @@
/**
* 头像数据 DTO
*/
import {
AvatarDataSchema,
type AvatarDataInput,
type AvatarDataData,
} from "@/data/schemas/user/avatar_data";
export class AvatarData {
declare readonly avatarUrl: string;
declare readonly userId: string;
private constructor(input: AvatarDataInput) {
const data = AvatarDataSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: AvatarDataInput): AvatarData {
return new AvatarData(input);
}
static fromJson(json: unknown): AvatarData {
return AvatarData.from(json as AvatarDataInput);
}
toJson(): AvatarDataData {
return AvatarDataSchema.parse(this);
}
}
+31
View File
@@ -0,0 +1,31 @@
/**
* 积分数据 DTO
*/
import {
CreditsDataSchema,
type CreditsDataInput,
type CreditsDataData,
} from "@/data/schemas/user/credits_data";
export class CreditsData {
declare readonly userId: string;
declare readonly dolBalance: number;
private constructor(input: CreditsDataInput) {
const data = CreditsDataSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: CreditsDataInput): CreditsData {
return new CreditsData(input);
}
static fromJson(json: unknown): CreditsData {
return CreditsData.from(json as CreditsDataInput);
}
toJson(): CreditsDataData {
return CreditsDataSchema.parse(this);
}
}
+33
View File
@@ -0,0 +1,33 @@
/**
* 积分历史记录 DTO
*/
import {
CreditsHistoryDataSchema,
type CreditsHistoryDataInput,
type CreditsHistoryDataData,
} from "@/data/schemas/user/credits_history_data";
export class CreditsHistoryData {
declare readonly records: Record<string, unknown>[];
declare readonly total: number;
declare readonly limit: number;
declare readonly offset: number;
private constructor(input: CreditsHistoryDataInput) {
const data = CreditsHistoryDataSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: CreditsHistoryDataInput): CreditsHistoryData {
return new CreditsHistoryData(input);
}
static fromJson(json: unknown): CreditsHistoryData {
return CreditsHistoryData.from(json as CreditsHistoryDataInput);
}
toJson(): CreditsHistoryDataData {
return CreditsHistoryDataSchema.parse(this);
}
}
+34
View File
@@ -0,0 +1,34 @@
/**
* 个性特征 DTO
*/
import {
PersonalityTraitsSchema,
type PersonalityTraitsInput,
type PersonalityTraitsData,
} from "@/data/schemas/user/personality_traits";
export class PersonalityTraits {
declare readonly cheerful: number;
declare readonly caring: number;
declare readonly playful: number;
declare readonly serious: number;
declare readonly romantic: number;
private constructor(input: PersonalityTraitsInput) {
const data = PersonalityTraitsSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: PersonalityTraitsInput): PersonalityTraits {
return new PersonalityTraits(input);
}
static fromJson(json: unknown): PersonalityTraits {
return PersonalityTraits.from(json as PersonalityTraitsInput);
}
toJson(): PersonalityTraitsData {
return PersonalityTraitsSchema.parse(this);
}
}
+33
View File
@@ -0,0 +1,33 @@
/**
* 最近记忆 DTO
*/
import {
RecentMemorySchema,
type RecentMemoryInput,
type RecentMemoryData,
} from "@/data/schemas/user/recent_memory";
export class RecentMemory {
declare readonly type: string;
declare readonly content: string;
declare readonly importance: number;
declare readonly createdAt: string;
private constructor(input: RecentMemoryInput) {
const data = RecentMemorySchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: RecentMemoryInput): RecentMemory {
return new RecentMemory(input);
}
static fromJson(json: unknown): RecentMemory {
return RecentMemory.from(json as RecentMemoryInput);
}
toJson(): RecentMemoryData {
return RecentMemorySchema.parse(this);
}
}
@@ -0,0 +1,33 @@
/**
* 更新个人资料请求 DTO
*/
import {
UpdateProfileRequestSchema,
type UpdateProfileRequestInput,
type UpdateProfileRequestData,
} from "@/data/schemas/user/update_profile_request";
export class UpdateProfileRequest {
declare readonly username: string;
declare readonly nickname: string;
declare readonly preferredLanguage: string;
declare readonly currentMood: string;
private constructor(input: UpdateProfileRequestInput) {
const data = UpdateProfileRequestSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: UpdateProfileRequestInput): UpdateProfileRequest {
return new UpdateProfileRequest(input);
}
static fromJson(json: unknown): UpdateProfileRequest {
return UpdateProfileRequest.from(json as UpdateProfileRequestInput);
}
toJson(): UpdateProfileRequestData {
return UpdateProfileRequestSchema.parse(this);
}
}
+59
View File
@@ -0,0 +1,59 @@
/**
* 用户 DTO
*/
import {
UserSchema,
type UserInput,
type UserData,
} from "@/data/schemas/user/user";
import { PersonalityTraits } from "./personality_traits";
export class User {
declare readonly id: string;
declare readonly username: string;
declare readonly email: string;
declare readonly platform: string;
declare readonly intimacy: number;
declare readonly dolBalance: number;
declare readonly relationshipStage: string;
declare readonly currentMood: string;
declare readonly personalityTraits: PersonalityTraits;
declare readonly preferredLanguage: string;
declare readonly createdAt: string;
declare readonly lastMessageAt: string;
declare readonly avatarUrl: string;
declare readonly loginProvider: string;
declare readonly isGuest: boolean;
private constructor(input: UserInput) {
const data = UserSchema.parse(input);
Object.assign(this, {
...data,
personalityTraits: PersonalityTraits.from(data.personalityTraits),
});
Object.freeze(this);
}
static from(input: UserInput): User {
return new User(input);
}
static fromJson(json: unknown): User {
return User.from(json as UserInput);
}
/**
* 创建一个具有空 id/username 的 User 实例(用于可选 user 字段的默认占位)
*/
static empty(): User {
return new User({ id: "", username: "" });
}
toJson(): UserData {
const { personalityTraits, ...rest } = this;
return {
...rest,
personalityTraits: personalityTraits.toJson(),
};
}
}
+51
View File
@@ -0,0 +1,51 @@
/**
* 用户统计响应 DTO
*/
import {
UserStatsResponseSchema,
type UserStatsResponseInput,
type UserStatsResponseData,
} from "@/data/schemas/user/user_stats_response";
import { PersonalityTraits } from "./personality_traits";
import { RecentMemory } from "./recent_memory";
export class UserStatsResponse {
declare readonly userId: string;
declare readonly username: string;
declare readonly platform: string;
declare readonly intimacy: number;
declare readonly relationshipStage: string;
declare readonly dolBalance: number;
declare readonly totalMessages: number;
declare readonly lastMessageAt: string;
declare readonly accountCreatedAt: string;
declare readonly currentMood: string;
declare readonly personalityTraits: PersonalityTraits;
declare readonly recentMemories: RecentMemory[];
declare readonly promptTokensTotal: number;
declare readonly completionTokensTotal: number;
declare readonly cacheHitTokensTotal: number;
declare readonly tokensTotal: number;
private constructor(input: UserStatsResponseInput) {
const data = UserStatsResponseSchema.parse(input);
Object.assign(this, {
...data,
personalityTraits: PersonalityTraits.from(data.personalityTraits),
recentMemories: data.recentMemories.map((m) => RecentMemory.from(m)),
});
Object.freeze(this);
}
static from(input: UserStatsResponseInput): UserStatsResponse {
return new UserStatsResponse(input);
}
static fromJson(json: unknown): UserStatsResponse {
return UserStatsResponse.from(json as UserStatsResponseInput);
}
toJson(): UserStatsResponseData {
return UserStatsResponseSchema.parse(this);
}
}