refactor(models): migrate 28 data models to Zod schema-driven pattern
This commit is contained in:
@@ -1,60 +1,41 @@
|
||||
/**
|
||||
* 聊天历史响应
|
||||
* 原始 Dart: ChatHistoryResponse (lib/data/models/chat/chat_response.dart)
|
||||
*
|
||||
* 注:原始 Dart 中 `total` 为 `int?`,按"全部非空"约定兜底为 0。
|
||||
*/
|
||||
import { ChatMessage } from "./chat_message";
|
||||
import { z } from "zod";
|
||||
import { ChatMessage, ChatMessageSchema } from "./chat_message";
|
||||
|
||||
export const ChatHistoryResponseSchema = z.object({
|
||||
messages: z.array(ChatMessageSchema),
|
||||
total: z.number().default(0),
|
||||
limit: z.number(),
|
||||
offset: z.number(),
|
||||
});
|
||||
|
||||
export type ChatHistoryResponseInput = z.input<typeof ChatHistoryResponseSchema>;
|
||||
export type ChatHistoryResponseData = z.output<typeof ChatHistoryResponseSchema>;
|
||||
|
||||
export class ChatHistoryResponse {
|
||||
readonly messages: ChatMessage[];
|
||||
readonly total: number;
|
||||
readonly limit: number;
|
||||
readonly offset: number;
|
||||
declare readonly messages: ChatMessage[];
|
||||
declare readonly total: number;
|
||||
declare readonly limit: number;
|
||||
declare readonly offset: number;
|
||||
|
||||
constructor(params: {
|
||||
messages: ChatMessage[];
|
||||
total?: number;
|
||||
limit: number;
|
||||
offset: number;
|
||||
}) {
|
||||
this.messages = params.messages;
|
||||
this.total = params.total ?? 0;
|
||||
this.limit = params.limit;
|
||||
this.offset = params.offset;
|
||||
private constructor(input: ChatHistoryResponseInput) {
|
||||
const data = ChatHistoryResponseSchema.parse(input);
|
||||
Object.assign(this, data);
|
||||
Object.freeze(this);
|
||||
}
|
||||
|
||||
toJson(): Record<string, unknown> {
|
||||
return {
|
||||
messages: this.messages.map((m) => m.toJson()),
|
||||
total: this.total,
|
||||
limit: this.limit,
|
||||
offset: this.offset,
|
||||
};
|
||||
static from(input: ChatHistoryResponseInput): ChatHistoryResponse {
|
||||
return new ChatHistoryResponse(input);
|
||||
}
|
||||
|
||||
static fromJson(json: Record<string, unknown>): ChatHistoryResponse {
|
||||
const requireNumber = (key: string): number => {
|
||||
const v = json[key];
|
||||
if (typeof v !== "number") {
|
||||
throw new Error(
|
||||
`ChatHistoryResponse.${key} is required and must be a number`
|
||||
);
|
||||
}
|
||||
return v;
|
||||
};
|
||||
const rawMessages = json.messages;
|
||||
const messages: ChatMessage[] = Array.isArray(rawMessages)
|
||||
? (rawMessages as Record<string, unknown>[]).map((m) =>
|
||||
ChatMessage.fromJson(m)
|
||||
)
|
||||
: [];
|
||||
return new ChatHistoryResponse({
|
||||
messages,
|
||||
total: typeof json.total === "number" ? json.total : undefined,
|
||||
limit: requireNumber("limit"),
|
||||
offset: requireNumber("offset"),
|
||||
});
|
||||
static fromJson(json: unknown): ChatHistoryResponse {
|
||||
return ChatHistoryResponse.from(json as ChatHistoryResponseInput);
|
||||
}
|
||||
|
||||
toJson(): ChatHistoryResponseData {
|
||||
return ChatHistoryResponseSchema.parse(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +1,40 @@
|
||||
/**
|
||||
* 聊天历史中的单条消息
|
||||
* 原始 Dart: ChatMessage (lib/data/models/chat/chat_response.dart)
|
||||
*
|
||||
* 注:原始 Dart 中 `id`/`createdAt` 为 `String?`,按"全部非空"约定兜底为空串。
|
||||
*/
|
||||
export class ChatMessage {
|
||||
readonly role: string;
|
||||
readonly content: string;
|
||||
readonly id: string;
|
||||
readonly createdAt: string;
|
||||
import { z } from "zod";
|
||||
|
||||
constructor(params: {
|
||||
role: string;
|
||||
content: string;
|
||||
id?: string;
|
||||
createdAt?: string;
|
||||
}) {
|
||||
this.role = params.role;
|
||||
this.content = params.content;
|
||||
this.id = params.id ?? "";
|
||||
this.createdAt = params.createdAt ?? "";
|
||||
export const ChatMessageSchema = z.object({
|
||||
role: z.string(),
|
||||
content: z.string(),
|
||||
id: z.string().default(""),
|
||||
createdAt: z.string().default(""),
|
||||
});
|
||||
|
||||
export type ChatMessageInput = z.input<typeof ChatMessageSchema>;
|
||||
export type ChatMessageData = z.output<typeof ChatMessageSchema>;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
toJson(): Record<string, unknown> {
|
||||
return {
|
||||
role: this.role,
|
||||
content: this.content,
|
||||
id: this.id,
|
||||
createdAt: this.createdAt,
|
||||
};
|
||||
static from(input: ChatMessageInput): ChatMessage {
|
||||
return new ChatMessage(input);
|
||||
}
|
||||
|
||||
static fromJson(json: Record<string, unknown>): ChatMessage {
|
||||
const requireString = (key: string): string => {
|
||||
const v = json[key];
|
||||
if (typeof v !== "string") {
|
||||
throw new Error(
|
||||
`ChatMessage.${key} is required and must be a string`
|
||||
);
|
||||
}
|
||||
return v;
|
||||
};
|
||||
return new ChatMessage({
|
||||
role: requireString("role"),
|
||||
content: requireString("content"),
|
||||
id: typeof json.id === "string" ? json.id : undefined,
|
||||
createdAt:
|
||||
typeof json.createdAt === "string" ? json.createdAt : undefined,
|
||||
});
|
||||
static fromJson(json: unknown): ChatMessage {
|
||||
return ChatMessage.from(json as ChatMessageInput);
|
||||
}
|
||||
|
||||
toJson(): ChatMessageData {
|
||||
return ChatMessageSchema.parse(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,98 +1,54 @@
|
||||
/**
|
||||
* 发送消息响应
|
||||
* 原始 Dart: ChatSendResponse (lib/data/models/chat/chat_response.dart)
|
||||
*
|
||||
* 注:原始 Dart 中 `mode`/`currentMood`/`isGuest` 为可空字段,按"全部非空"约定兜底:
|
||||
* - 字符串 → ""
|
||||
* - bool → false
|
||||
* - int → 0
|
||||
*/
|
||||
export class ChatSendResponse {
|
||||
readonly mode: string;
|
||||
readonly reply: string;
|
||||
readonly voiceUrl: string;
|
||||
readonly audioUrl: string;
|
||||
readonly intimacyChange: number;
|
||||
readonly newIntimacy: number;
|
||||
readonly relationshipStage: string;
|
||||
readonly currentMood: string;
|
||||
readonly messageId: string;
|
||||
readonly isGuest: boolean;
|
||||
readonly timestamp: number;
|
||||
import { z } from "zod";
|
||||
|
||||
constructor(params: {
|
||||
mode?: string;
|
||||
reply: string;
|
||||
voiceUrl?: string;
|
||||
audioUrl?: string;
|
||||
intimacyChange?: number;
|
||||
newIntimacy?: number;
|
||||
relationshipStage: string;
|
||||
currentMood?: string;
|
||||
messageId: string;
|
||||
isGuest?: boolean;
|
||||
timestamp?: number;
|
||||
}) {
|
||||
this.mode = params.mode ?? "";
|
||||
this.reply = params.reply;
|
||||
this.voiceUrl = params.voiceUrl ?? "";
|
||||
this.audioUrl = params.audioUrl ?? "";
|
||||
this.intimacyChange = params.intimacyChange ?? 0;
|
||||
this.newIntimacy = params.newIntimacy ?? 0;
|
||||
this.relationshipStage = params.relationshipStage;
|
||||
this.currentMood = params.currentMood ?? "";
|
||||
this.messageId = params.messageId;
|
||||
this.isGuest = params.isGuest ?? false;
|
||||
this.timestamp = params.timestamp ?? 0;
|
||||
export const ChatSendResponseSchema = z.object({
|
||||
mode: z.string().default(""),
|
||||
reply: z.string(),
|
||||
voiceUrl: z.string().default(""),
|
||||
audioUrl: z.string().default(""),
|
||||
intimacyChange: z.number().default(0),
|
||||
newIntimacy: z.number().default(0),
|
||||
relationshipStage: z.string(),
|
||||
currentMood: z.string().default(""),
|
||||
messageId: z.string(),
|
||||
isGuest: z.boolean().default(false),
|
||||
timestamp: z.number().default(0),
|
||||
});
|
||||
|
||||
export type ChatSendResponseInput = z.input<typeof ChatSendResponseSchema>;
|
||||
export type ChatSendResponseData = z.output<typeof ChatSendResponseSchema>;
|
||||
|
||||
export class ChatSendResponse {
|
||||
declare readonly mode: string;
|
||||
declare readonly reply: string;
|
||||
declare readonly voiceUrl: string;
|
||||
declare readonly audioUrl: string;
|
||||
declare readonly intimacyChange: 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);
|
||||
}
|
||||
|
||||
toJson(): Record<string, unknown> {
|
||||
return {
|
||||
mode: this.mode,
|
||||
reply: this.reply,
|
||||
voiceUrl: this.voiceUrl,
|
||||
audioUrl: this.audioUrl,
|
||||
intimacyChange: this.intimacyChange,
|
||||
newIntimacy: this.newIntimacy,
|
||||
relationshipStage: this.relationshipStage,
|
||||
currentMood: this.currentMood,
|
||||
messageId: this.messageId,
|
||||
isGuest: this.isGuest,
|
||||
timestamp: this.timestamp,
|
||||
};
|
||||
static from(input: ChatSendResponseInput): ChatSendResponse {
|
||||
return new ChatSendResponse(input);
|
||||
}
|
||||
|
||||
static fromJson(json: Record<string, unknown>): ChatSendResponse {
|
||||
const requireString = (key: string): string => {
|
||||
const v = json[key];
|
||||
if (typeof v !== "string") {
|
||||
throw new Error(
|
||||
`ChatSendResponse.${key} is required and must be a string`
|
||||
);
|
||||
}
|
||||
return v;
|
||||
};
|
||||
return new ChatSendResponse({
|
||||
mode: typeof json.mode === "string" ? json.mode : undefined,
|
||||
reply: requireString("reply"),
|
||||
voiceUrl:
|
||||
typeof json.voiceUrl === "string" ? json.voiceUrl : undefined,
|
||||
audioUrl:
|
||||
typeof json.audioUrl === "string" ? json.audioUrl : undefined,
|
||||
intimacyChange:
|
||||
typeof json.intimacyChange === "number"
|
||||
? json.intimacyChange
|
||||
: undefined,
|
||||
newIntimacy:
|
||||
typeof json.newIntimacy === "number" ? json.newIntimacy : undefined,
|
||||
relationshipStage: requireString("relationshipStage"),
|
||||
currentMood:
|
||||
typeof json.currentMood === "string" ? json.currentMood : undefined,
|
||||
messageId: requireString("messageId"),
|
||||
isGuest: typeof json.isGuest === "boolean" ? json.isGuest : undefined,
|
||||
timestamp:
|
||||
typeof json.timestamp === "number" ? json.timestamp : undefined,
|
||||
});
|
||||
static fromJson(json: unknown): ChatSendResponse {
|
||||
return ChatSendResponse.from(json as ChatSendResponseInput);
|
||||
}
|
||||
|
||||
toJson(): ChatSendResponseData {
|
||||
return ChatSendResponseSchema.parse(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,36 +2,35 @@
|
||||
* 消息同步响应
|
||||
* 原始 Dart: ChatSyncData (lib/data/models/chat/chat_sync.dart)
|
||||
*/
|
||||
export class ChatSyncData {
|
||||
readonly syncedCount: number;
|
||||
readonly totalHistory: number;
|
||||
import { z } from "zod";
|
||||
|
||||
constructor(params: { syncedCount: number; totalHistory: number }) {
|
||||
this.syncedCount = params.syncedCount;
|
||||
this.totalHistory = params.totalHistory;
|
||||
export const ChatSyncDataSchema = z.object({
|
||||
syncedCount: z.number(),
|
||||
totalHistory: z.number(),
|
||||
});
|
||||
|
||||
export type ChatSyncDataInput = z.input<typeof ChatSyncDataSchema>;
|
||||
export type ChatSyncDataData = z.output<typeof ChatSyncDataSchema>;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
toJson(): Record<string, unknown> {
|
||||
return {
|
||||
syncedCount: this.syncedCount,
|
||||
totalHistory: this.totalHistory,
|
||||
};
|
||||
static from(input: ChatSyncDataInput): ChatSyncData {
|
||||
return new ChatSyncData(input);
|
||||
}
|
||||
|
||||
static fromJson(json: Record<string, unknown>): ChatSyncData {
|
||||
const requireNumber = (key: string): number => {
|
||||
const v = json[key];
|
||||
if (typeof v !== "number") {
|
||||
throw new Error(
|
||||
`ChatSyncData.${key} is required and must be a number`
|
||||
);
|
||||
}
|
||||
return v;
|
||||
};
|
||||
return new ChatSyncData({
|
||||
syncedCount: requireNumber("syncedCount"),
|
||||
totalHistory: requireNumber("totalHistory"),
|
||||
});
|
||||
static fromJson(json: unknown): ChatSyncData {
|
||||
return ChatSyncData.from(json as ChatSyncDataInput);
|
||||
}
|
||||
|
||||
toJson(): ChatSyncDataData {
|
||||
return ChatSyncDataSchema.parse(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,27 +2,34 @@
|
||||
* 消息同步请求
|
||||
* 原始 Dart: ChatSyncRequest (lib/data/models/chat/chat_sync.dart)
|
||||
*/
|
||||
import { SyncMessage } from "./sync_message";
|
||||
import { z } from "zod";
|
||||
import { SyncMessage, SyncMessageSchema } from "./sync_message";
|
||||
|
||||
export const ChatSyncRequestSchema = z.object({
|
||||
messages: z.array(SyncMessageSchema),
|
||||
});
|
||||
|
||||
export type ChatSyncRequestInput = z.input<typeof ChatSyncRequestSchema>;
|
||||
export type ChatSyncRequestData = z.output<typeof ChatSyncRequestSchema>;
|
||||
|
||||
export class ChatSyncRequest {
|
||||
readonly messages: SyncMessage[];
|
||||
declare readonly messages: SyncMessage[];
|
||||
|
||||
constructor(params: { messages: SyncMessage[] }) {
|
||||
this.messages = params.messages;
|
||||
private constructor(input: ChatSyncRequestInput) {
|
||||
const data = ChatSyncRequestSchema.parse(input);
|
||||
Object.assign(this, data);
|
||||
Object.freeze(this);
|
||||
}
|
||||
|
||||
toJson(): Record<string, unknown> {
|
||||
return { messages: this.messages.map((m) => m.toJson()) };
|
||||
static from(input: ChatSyncRequestInput): ChatSyncRequest {
|
||||
return new ChatSyncRequest(input);
|
||||
}
|
||||
|
||||
static fromJson(json: Record<string, unknown>): ChatSyncRequest {
|
||||
const rawMessages = json.messages;
|
||||
const messages: SyncMessage[] = Array.isArray(rawMessages)
|
||||
? (rawMessages as Record<string, unknown>[]).map((m) =>
|
||||
SyncMessage.fromJson(m)
|
||||
)
|
||||
: [];
|
||||
return new ChatSyncRequest({ messages });
|
||||
static fromJson(json: unknown): ChatSyncRequest {
|
||||
return ChatSyncRequest.from(json as ChatSyncRequestInput);
|
||||
}
|
||||
|
||||
toJson(): ChatSyncRequestData {
|
||||
return ChatSyncRequestSchema.parse(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,23 @@
|
||||
* 游客每日聊天配额数据
|
||||
* 原始 Dart: GuestChatQuota (lib/data/models/chat/guest_chat_quota.dart)
|
||||
*
|
||||
* 注:原始 Dart 中的静态常量与运行时环境判断方法(isDevelopment)已迁移为
|
||||
* 静态成员。环境检测逻辑(threshold / totalQuotaDefault 等)保留为方法,
|
||||
* 内部使用 `process.env.NODE_ENV` 替代 Dart 中的 `AppEnvUtil.isDevelopment`。
|
||||
* `initial()` 工厂方法已迁移为静态方法;`needReset` getter 保留为实例方法。
|
||||
*
|
||||
* Dart 中 `initial()` 依赖 `app_date.DateUtils.todayString()`,
|
||||
* 在 TS 中由调用方通过 `date` 字段传入。
|
||||
* 注:保留原始 Dart 中的静态常量与运行时环境判断方法。
|
||||
* `process.env.NODE_ENV` 替代 Dart 的 `AppEnvUtil.isDevelopment`。
|
||||
* `initial()` 工厂方法与 `needReset` 实例方法保留。
|
||||
*/
|
||||
import { z } from "zod";
|
||||
|
||||
export const GuestChatQuotaSchema = z.object({
|
||||
remaining: z.number(),
|
||||
date: z.string(),
|
||||
});
|
||||
|
||||
export type GuestChatQuotaInput = z.input<typeof GuestChatQuotaSchema>;
|
||||
export type GuestChatQuotaData = z.output<typeof GuestChatQuotaSchema>;
|
||||
|
||||
export class GuestChatQuota {
|
||||
readonly remaining: number;
|
||||
readonly date: string;
|
||||
declare readonly remaining: number;
|
||||
declare readonly date: string;
|
||||
|
||||
// 静态常量
|
||||
static readonly maxQuotaPerDay = 40;
|
||||
@@ -23,12 +29,24 @@ export class GuestChatQuota {
|
||||
static readonly defaultTotalQuotaDev = 5;
|
||||
static readonly quotaExhausted = 0;
|
||||
|
||||
constructor(params: { remaining: number; date: string }) {
|
||||
this.remaining = params.remaining;
|
||||
this.date = params.date;
|
||||
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);
|
||||
}
|
||||
|
||||
toJson(): GuestChatQuotaData {
|
||||
return GuestChatQuotaSchema.parse(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 配额耗尽状态值(便捷引用)
|
||||
*/
|
||||
@@ -89,25 +107,4 @@ export class GuestChatQuota {
|
||||
needsReset(todayString: string): boolean {
|
||||
return this.date !== todayString;
|
||||
}
|
||||
|
||||
toJson(): Record<string, unknown> {
|
||||
return {
|
||||
remaining: this.remaining,
|
||||
date: this.date,
|
||||
};
|
||||
}
|
||||
|
||||
static fromJson(json: Record<string, unknown>): GuestChatQuota {
|
||||
const remaining = json.remaining;
|
||||
const date = json.date;
|
||||
if (typeof remaining !== "number") {
|
||||
throw new Error(
|
||||
"GuestChatQuota.remaining is required and must be a number"
|
||||
);
|
||||
}
|
||||
if (typeof date !== "string") {
|
||||
throw new Error("GuestChatQuota.date is required and must be a string");
|
||||
}
|
||||
return new GuestChatQuota({ remaining, date });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,78 +2,47 @@
|
||||
* 图片上传响应
|
||||
* 原始 Dart: ImageUploadResponse (lib/data/models/chat/image_upload_response.dart)
|
||||
*/
|
||||
export class ImageUploadResponse {
|
||||
readonly success: boolean;
|
||||
readonly imageId: string;
|
||||
readonly thumbUrl: string;
|
||||
readonly mediumUrl: string;
|
||||
readonly originalUrl: string;
|
||||
readonly width: number;
|
||||
readonly height: number;
|
||||
readonly bytes: number;
|
||||
import { z } from "zod";
|
||||
|
||||
constructor(params: {
|
||||
success?: boolean;
|
||||
imageId: string;
|
||||
thumbUrl: string;
|
||||
mediumUrl: string;
|
||||
originalUrl: string;
|
||||
width: number;
|
||||
height: number;
|
||||
bytes: number;
|
||||
}) {
|
||||
this.success = params.success ?? true;
|
||||
this.imageId = params.imageId;
|
||||
this.thumbUrl = params.thumbUrl;
|
||||
this.mediumUrl = params.mediumUrl;
|
||||
this.originalUrl = params.originalUrl;
|
||||
this.width = params.width;
|
||||
this.height = params.height;
|
||||
this.bytes = params.bytes;
|
||||
export const ImageUploadResponseSchema = z.object({
|
||||
success: z.boolean().default(true),
|
||||
imageId: z.string(),
|
||||
thumbUrl: z.string(),
|
||||
mediumUrl: z.string(),
|
||||
originalUrl: z.string(),
|
||||
width: z.number(),
|
||||
height: z.number(),
|
||||
bytes: z.number(),
|
||||
});
|
||||
|
||||
export type ImageUploadResponseInput = z.input<typeof ImageUploadResponseSchema>;
|
||||
export type ImageUploadResponseData = z.output<typeof ImageUploadResponseSchema>;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
toJson(): Record<string, unknown> {
|
||||
return {
|
||||
success: this.success,
|
||||
imageId: this.imageId,
|
||||
thumbUrl: this.thumbUrl,
|
||||
mediumUrl: this.mediumUrl,
|
||||
originalUrl: this.originalUrl,
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
bytes: this.bytes,
|
||||
};
|
||||
static from(input: ImageUploadResponseInput): ImageUploadResponse {
|
||||
return new ImageUploadResponse(input);
|
||||
}
|
||||
|
||||
static fromJson(json: Record<string, unknown>): ImageUploadResponse {
|
||||
const requireString = (key: string): string => {
|
||||
const v = json[key];
|
||||
if (typeof v !== "string") {
|
||||
throw new Error(
|
||||
`ImageUploadResponse.${key} is required and must be a string`
|
||||
);
|
||||
}
|
||||
return v;
|
||||
};
|
||||
const requireNumber = (key: string): number => {
|
||||
const v = json[key];
|
||||
if (typeof v !== "number") {
|
||||
throw new Error(
|
||||
`ImageUploadResponse.${key} is required and must be a number`
|
||||
);
|
||||
}
|
||||
return v;
|
||||
};
|
||||
return new ImageUploadResponse({
|
||||
success: typeof json.success === "boolean" ? json.success : undefined,
|
||||
imageId: requireString("imageId"),
|
||||
thumbUrl: requireString("thumbUrl"),
|
||||
mediumUrl: requireString("mediumUrl"),
|
||||
originalUrl: requireString("originalUrl"),
|
||||
width: requireNumber("width"),
|
||||
height: requireNumber("height"),
|
||||
bytes: requireNumber("bytes"),
|
||||
});
|
||||
static fromJson(json: unknown): ImageUploadResponse {
|
||||
return ImageUploadResponse.from(json as ImageUploadResponseInput);
|
||||
}
|
||||
|
||||
toJson(): ImageUploadResponseData {
|
||||
return ImageUploadResponseSchema.parse(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,82 +1,50 @@
|
||||
/**
|
||||
* 发送消息请求 - 用于 /api/chat/send 接口
|
||||
* 原始 Dart: SendMessageRequest (lib/data/models/chat/send_message_request.dart)
|
||||
*
|
||||
* 注:原始 Dart 中所有图片相关字段为可空,按"全部非空"约定兜底:
|
||||
* - 字符串 → ""
|
||||
* - 数字 → 0
|
||||
*/
|
||||
export class SendMessageRequest {
|
||||
readonly message: string;
|
||||
readonly image: string;
|
||||
readonly imageId: string;
|
||||
readonly imageThumbUrl: string;
|
||||
readonly imageMediumUrl: string;
|
||||
readonly imageOriginalUrl: string;
|
||||
readonly imageWidth: number;
|
||||
readonly imageHeight: number;
|
||||
readonly useWebSocket: boolean;
|
||||
import { z } from "zod";
|
||||
|
||||
constructor(params: {
|
||||
message?: string;
|
||||
image?: string;
|
||||
imageId?: string;
|
||||
imageThumbUrl?: string;
|
||||
imageMediumUrl?: string;
|
||||
imageOriginalUrl?: string;
|
||||
imageWidth?: number;
|
||||
imageHeight?: number;
|
||||
useWebSocket?: boolean;
|
||||
}) {
|
||||
this.message = params.message ?? "";
|
||||
this.image = params.image ?? "";
|
||||
this.imageId = params.imageId ?? "";
|
||||
this.imageThumbUrl = params.imageThumbUrl ?? "";
|
||||
this.imageMediumUrl = params.imageMediumUrl ?? "";
|
||||
this.imageOriginalUrl = params.imageOriginalUrl ?? "";
|
||||
this.imageWidth = params.imageWidth ?? 0;
|
||||
this.imageHeight = params.imageHeight ?? 0;
|
||||
this.useWebSocket = params.useWebSocket ?? false;
|
||||
export const SendMessageRequestSchema = z.object({
|
||||
message: z.string().default(""),
|
||||
image: z.string().default(""),
|
||||
imageId: z.string().default(""),
|
||||
imageThumbUrl: z.string().default(""),
|
||||
imageMediumUrl: z.string().default(""),
|
||||
imageOriginalUrl: z.string().default(""),
|
||||
imageWidth: z.number().default(0),
|
||||
imageHeight: z.number().default(0),
|
||||
useWebSocket: z.boolean().default(false),
|
||||
});
|
||||
|
||||
export type SendMessageRequestInput = z.input<typeof SendMessageRequestSchema>;
|
||||
export type SendMessageRequestData = z.output<typeof SendMessageRequestSchema>;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
toJson(): Record<string, unknown> {
|
||||
return {
|
||||
message: this.message,
|
||||
image: this.image,
|
||||
imageId: this.imageId,
|
||||
imageThumbUrl: this.imageThumbUrl,
|
||||
imageMediumUrl: this.imageMediumUrl,
|
||||
imageOriginalUrl: this.imageOriginalUrl,
|
||||
imageWidth: this.imageWidth,
|
||||
imageHeight: this.imageHeight,
|
||||
useWebSocket: this.useWebSocket,
|
||||
};
|
||||
static from(input: SendMessageRequestInput): SendMessageRequest {
|
||||
return new SendMessageRequest(input);
|
||||
}
|
||||
|
||||
static fromJson(json: Record<string, unknown>): SendMessageRequest {
|
||||
return new SendMessageRequest({
|
||||
message: typeof json.message === "string" ? json.message : undefined,
|
||||
image: typeof json.image === "string" ? json.image : undefined,
|
||||
imageId: typeof json.imageId === "string" ? json.imageId : undefined,
|
||||
imageThumbUrl:
|
||||
typeof json.imageThumbUrl === "string"
|
||||
? json.imageThumbUrl
|
||||
: undefined,
|
||||
imageMediumUrl:
|
||||
typeof json.imageMediumUrl === "string"
|
||||
? json.imageMediumUrl
|
||||
: undefined,
|
||||
imageOriginalUrl:
|
||||
typeof json.imageOriginalUrl === "string"
|
||||
? json.imageOriginalUrl
|
||||
: undefined,
|
||||
imageWidth:
|
||||
typeof json.imageWidth === "number" ? json.imageWidth : undefined,
|
||||
imageHeight:
|
||||
typeof json.imageHeight === "number" ? json.imageHeight : undefined,
|
||||
useWebSocket:
|
||||
typeof json.useWebSocket === "boolean" ? json.useWebSocket : undefined,
|
||||
});
|
||||
static fromJson(json: unknown): SendMessageRequest {
|
||||
return SendMessageRequest.from(json as SendMessageRequestInput);
|
||||
}
|
||||
|
||||
toJson(): SendMessageRequestData {
|
||||
return SendMessageRequestSchema.parse(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,23 +2,33 @@
|
||||
* STT(语音转文字)响应
|
||||
* 原始 Dart: SttData (lib/data/models/chat/stt_response.dart)
|
||||
*/
|
||||
export class SttData {
|
||||
readonly text: string;
|
||||
import { z } from "zod";
|
||||
|
||||
constructor(params: { text: string }) {
|
||||
this.text = params.text;
|
||||
export const SttDataSchema = z.object({
|
||||
text: z.string(),
|
||||
});
|
||||
|
||||
export type SttDataInput = z.input<typeof SttDataSchema>;
|
||||
export type SttDataData = z.output<typeof SttDataSchema>;
|
||||
|
||||
export class SttData {
|
||||
declare readonly text: string;
|
||||
|
||||
private constructor(input: SttDataInput) {
|
||||
const data = SttDataSchema.parse(input);
|
||||
Object.assign(this, data);
|
||||
Object.freeze(this);
|
||||
}
|
||||
|
||||
toJson(): Record<string, unknown> {
|
||||
return { text: this.text };
|
||||
static from(input: SttDataInput): SttData {
|
||||
return new SttData(input);
|
||||
}
|
||||
|
||||
static fromJson(json: Record<string, unknown>): SttData {
|
||||
const text = json.text;
|
||||
if (typeof text !== "string") {
|
||||
throw new Error("SttData.text is required and must be a string");
|
||||
}
|
||||
return new SttData({ text });
|
||||
static fromJson(json: unknown): SttData {
|
||||
return SttData.from(json as SttDataInput);
|
||||
}
|
||||
|
||||
toJson(): SttDataData {
|
||||
return SttDataSchema.parse(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,40 +2,37 @@
|
||||
* 待同步的单条消息
|
||||
* 原始 Dart: SyncMessage (lib/data/models/chat/chat_sync.dart)
|
||||
*/
|
||||
export class SyncMessage {
|
||||
readonly role: string;
|
||||
readonly content: string;
|
||||
readonly timestamp: string;
|
||||
import { z } from "zod";
|
||||
|
||||
constructor(params: { role: string; content: string; timestamp: string }) {
|
||||
this.role = params.role;
|
||||
this.content = params.content;
|
||||
this.timestamp = params.timestamp;
|
||||
export const SyncMessageSchema = z.object({
|
||||
role: z.string(),
|
||||
content: z.string(),
|
||||
timestamp: z.string(),
|
||||
});
|
||||
|
||||
export type SyncMessageInput = z.input<typeof SyncMessageSchema>;
|
||||
export type SyncMessageData = z.output<typeof SyncMessageSchema>;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
toJson(): Record<string, unknown> {
|
||||
return {
|
||||
role: this.role,
|
||||
content: this.content,
|
||||
timestamp: this.timestamp,
|
||||
};
|
||||
static from(input: SyncMessageInput): SyncMessage {
|
||||
return new SyncMessage(input);
|
||||
}
|
||||
|
||||
static fromJson(json: Record<string, unknown>): SyncMessage {
|
||||
const requireString = (key: string): string => {
|
||||
const v = json[key];
|
||||
if (typeof v !== "string") {
|
||||
throw new Error(
|
||||
`SyncMessage.${key} is required and must be a string`
|
||||
);
|
||||
}
|
||||
return v;
|
||||
};
|
||||
return new SyncMessage({
|
||||
role: requireString("role"),
|
||||
content: requireString("content"),
|
||||
timestamp: requireString("timestamp"),
|
||||
});
|
||||
static fromJson(json: unknown): SyncMessage {
|
||||
return SyncMessage.from(json as SyncMessageInput);
|
||||
}
|
||||
|
||||
toJson(): SyncMessageData {
|
||||
return SyncMessageSchema.parse(this);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user