refactor(models): migrate 28 data models to Zod schema-driven pattern
This commit is contained in:
@@ -0,0 +1,228 @@
|
|||||||
|
# Implementation Plan: Zod 重构数据模型
|
||||||
|
|
||||||
|
## [Overview]
|
||||||
|
|
||||||
|
将 `src/data/models/` 下 28 个数据类从「手写 `readonly` + 手动 `??` 兜底 + `fromJson` 守卫」模式重构为「Zod schema 驱动 + 类包装」模式,样板代码量减少约 70%,同时获得运行时验证能力。
|
||||||
|
|
||||||
|
## [Types]
|
||||||
|
|
||||||
|
### Schema 类型(每个数据类三个导出)
|
||||||
|
|
||||||
|
每个数据类使用同一份 Zod schema 同时驱动**验证、类型推导、默认值兜底**:
|
||||||
|
|
||||||
|
| 导出 | 来源 | 用途 |
|
||||||
|
|---|---|---|
|
||||||
|
| `XxxSchema` | `z.object({...})` | 验证规则与默认值定义 |
|
||||||
|
| `XxxInput` | `z.input<typeof XxxSchema>` | 构造输入类型(字段可缺失,触发 `.default()`) |
|
||||||
|
| `XxxData` | `z.output<typeof XxxSchema>` | 解析后类型(所有字段已应用默认值) |
|
||||||
|
| `class Xxx` | 手写 + `Object.assign` | 不可变类,保留自定义方法 |
|
||||||
|
|
||||||
|
### Zod 字段类型映射
|
||||||
|
|
||||||
|
| Dart 字段 | Zod schema |
|
||||||
|
|---|---|
|
||||||
|
| `required String id` | `z.string()` |
|
||||||
|
| `@Default('web') String platform` | `z.string().default('web')` |
|
||||||
|
| `String? email` | `z.string().default('')` |
|
||||||
|
| `@Default(0) int intimacy` | `z.number().default(0)` |
|
||||||
|
| `bool? isGuest` | `z.boolean().default(false)` |
|
||||||
|
| `required List<T> items` | `z.array(TItemSchema)` |
|
||||||
|
| `PersonalityTraits? personalityTraits` | `PersonalityTraitsSchema.default({})` |
|
||||||
|
| `Map<String, dynamic>` | `z.record(z.string(), z.unknown())` |
|
||||||
|
|
||||||
|
### 28 个类清单
|
||||||
|
|
||||||
|
**user/ (8)**: PersonalityTraits, User, CreditsData, CreditsHistoryData, AvatarData, UpdateProfileRequest, RecentMemory, UserStatsResponse
|
||||||
|
|
||||||
|
**auth/ 请求 (9)**: SendCodeRequest, RegisterRequest, GoogleLoginRequest, FacebookLoginRequest, GuestLoginRequest, LoginRequest, AppleLoginRequest, RefreshTokenRequest, FbIdLoginRequest
|
||||||
|
|
||||||
|
**auth/ 响应 (4)**: LoginResponse, GuestLoginResponse, LogoutResponse, RefreshTokenResponse
|
||||||
|
|
||||||
|
**chat/ (10)**: ChatMessage, ChatSendResponse, ChatHistoryResponse, ChatSyncRequest, ChatSyncData, SyncMessage, GuestChatQuota, ImageUploadResponse, SendMessageRequest, SttData
|
||||||
|
|
||||||
|
**metrics/ (2)**: AppEvent, PwaEvent
|
||||||
|
|
||||||
|
## [Files]
|
||||||
|
|
||||||
|
### 新增
|
||||||
|
|
||||||
|
| 路径 | 用途 |
|
||||||
|
|---|---|
|
||||||
|
| `implementation_plan.md` | 本文档 |
|
||||||
|
|
||||||
|
### 修改
|
||||||
|
|
||||||
|
| 路径 | 修改 |
|
||||||
|
|---|---|
|
||||||
|
| `package.json` | 添加 `zod: ^3.23.0` 依赖 |
|
||||||
|
| `src/data/models/**/*.ts` | 28 个模型文件全部重构为 Zod 驱动 + 类包装模式 |
|
||||||
|
| `src/data/models/index.ts` | 不变(仍然 `export * from "./..."`) |
|
||||||
|
| `src/data/models/auth/index.ts` | 不变 |
|
||||||
|
| `src/data/models/chat/index.ts` | 不变 |
|
||||||
|
| `src/data/models/metrics/index.ts` | 不变 |
|
||||||
|
| `src/data/models/user/index.ts` | 不变 |
|
||||||
|
| `pnpm-lock.yaml` | `pnpm install` 自动更新 |
|
||||||
|
|
||||||
|
### 删除
|
||||||
|
|
||||||
|
无(保留原文件结构,重写内容)
|
||||||
|
|
||||||
|
## [Functions]
|
||||||
|
|
||||||
|
### 移除的样板函数(每个类)
|
||||||
|
|
||||||
|
| 函数 | 位置 | 移除原因 |
|
||||||
|
|---|---|---|
|
||||||
|
| `requireString` 局部辅助 | 每个 `fromJson` | 由 Zod schema 验证替代 |
|
||||||
|
| `requireNumber` 局部辅助 | 每个 `fromJson` | 由 Zod schema 验证替代 |
|
||||||
|
| 构造函数中 `params.x ?? defaultX` ×N | 每个类 | 由 `.default()` schema 替代 |
|
||||||
|
|
||||||
|
### 保留/调整的方法
|
||||||
|
|
||||||
|
| 方法 | 签名 | 变化 |
|
||||||
|
|---|---|---|
|
||||||
|
| `static fromJson(json)` | `(json: unknown) => Xxx` | 接收 `unknown`(更安全),内部 `Schema.parse(json)` |
|
||||||
|
| `toJson()` | `() => XxxData` | 返回 `Schema.parse(this)`,保证输出一致性 |
|
||||||
|
| 静态常量(`GuestChatQuota`) | `static readonly X` | 不变 |
|
||||||
|
| 静态方法(`GuestChatQuota.threshold` 等) | `static get X()` | 不变 |
|
||||||
|
| 实例方法(`GuestChatQuota.needsReset`) | `(todayString: string) => boolean` | 不变 |
|
||||||
|
|
||||||
|
## [Classes]
|
||||||
|
|
||||||
|
### 重构模式(统一范式)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
// 1. Schema(单一数据源)
|
||||||
|
export const XxxSchema = z.object({
|
||||||
|
field1: z.string(),
|
||||||
|
field2: z.number().default(0),
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
|
||||||
|
export type XxxInput = z.input<typeof XxxSchema>;
|
||||||
|
export type XxxData = z.output<typeof XxxSchema>;
|
||||||
|
|
||||||
|
// 2. 类(包装 + 不可变 + 自定义方法)
|
||||||
|
export class Xxx {
|
||||||
|
// 仅类型声明,无运行时初始化
|
||||||
|
declare readonly field1: string;
|
||||||
|
declare readonly field2: number;
|
||||||
|
// ...
|
||||||
|
|
||||||
|
private constructor(data: XxxData) {
|
||||||
|
Object.assign(this, data); // 运行时填值
|
||||||
|
Object.freeze(this); // 不可变
|
||||||
|
}
|
||||||
|
|
||||||
|
static from(input: XxxInput): Xxx {
|
||||||
|
return new Xxx(XxxSchema.parse(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJson(json: unknown): Xxx {
|
||||||
|
return Xxx.from(json as XxxInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
toJson(): XxxData {
|
||||||
|
return XxxSchema.parse(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 28 个类的具体调整
|
||||||
|
|
||||||
|
| 类 | 特殊处理 |
|
||||||
|
|---|---|
|
||||||
|
| `PersonalityTraits` | 嵌套引用基类,5 个 double 字段各有 `.default(0.5)` |
|
||||||
|
| `User` | 引用 `PersonalityTraitsSchema.default({})` |
|
||||||
|
| `UserStatsResponse` | 引用 `PersonalityTraitsSchema` + `RecentMemory` 数组 |
|
||||||
|
| `LoginResponse` | 引用 `User` |
|
||||||
|
| `GuestLoginResponse` | 引用 `User`(默认值 `new User({id:"",username:""})` 仍需保留逻辑) |
|
||||||
|
| `ChatHistoryResponse` | 数组字段 `z.array(ChatMessageSchema)` |
|
||||||
|
| `ChatSyncRequest` | 数组字段 `z.array(SyncMessageSchema)` |
|
||||||
|
| `CreditsHistoryData` | `records: z.array(z.record(z.string(), z.unknown()))` |
|
||||||
|
| `GuestChatQuota` | 保留所有静态常量/方法/实例方法 |
|
||||||
|
| 其他 19 个 | 简单结构,直接映射 |
|
||||||
|
|
||||||
|
## [Dependencies]
|
||||||
|
|
||||||
|
### 新增
|
||||||
|
|
||||||
|
| 包 | 版本 | 用途 |
|
||||||
|
|---|---|---|
|
||||||
|
| `zod` | `^3.23.0` | 模式验证与类型推导 |
|
||||||
|
|
||||||
|
### 安装命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm add zod
|
||||||
|
```
|
||||||
|
|
||||||
|
### 版本选择
|
||||||
|
|
||||||
|
选择 zod v3(最新稳定版),原因:
|
||||||
|
- 文档完善,社区生态成熟
|
||||||
|
- 与 TypeScript 5 完全兼容
|
||||||
|
- 体积适中(按需 tree-shake 后约 10-15KB)
|
||||||
|
- v4 仍在 beta,暂不采用
|
||||||
|
|
||||||
|
## [Testing]
|
||||||
|
|
||||||
|
### 验证策略
|
||||||
|
|
||||||
|
每个批次完成后:
|
||||||
|
- `npx tsc --noEmit` — 0 错误
|
||||||
|
- `pnpm lint` — 通过
|
||||||
|
|
||||||
|
全部完成后进行端到端测试:
|
||||||
|
- 构造最小输入(仅 required 字段)→ `Model.fromJson({...})` → 验证默认值已应用
|
||||||
|
- 构造完整输入 → `Model.fromJson({...})` → `.toJson()` → 验证往返一致性
|
||||||
|
- 构造无效输入(缺失 required 字段)→ `Model.fromJson({...})` → 验证抛出 `ZodError`
|
||||||
|
|
||||||
|
### 端到端测试脚本(临时)
|
||||||
|
|
||||||
|
实施完成后在 `src/_test_zod.ts` 写入以下验证代码(完成后删除):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { User, ChatMessage, AppEvent } from "@/data/models";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
// 1. 最小输入 + 默认值
|
||||||
|
const u1 = User.fromJson({ id: "u1", username: "alice" });
|
||||||
|
console.assert(u1.email === "");
|
||||||
|
console.assert(u1.platform === "web");
|
||||||
|
console.assert(u1.intimacy === 0);
|
||||||
|
console.assert(u1.personalityTraits.cheerful === 0.5);
|
||||||
|
|
||||||
|
// 2. 完整输入 + 往返
|
||||||
|
const original = { id: "u1", username: "alice", email: "a@b.com", intimacy: 10 };
|
||||||
|
const u2 = User.fromJson(original);
|
||||||
|
const json = u2.toJson();
|
||||||
|
console.assert(JSON.stringify(json) === JSON.stringify({...original, platform: "web", dolBalance: 0, ...}));
|
||||||
|
|
||||||
|
// 3. 无效输入抛错
|
||||||
|
try {
|
||||||
|
User.fromJson({ id: "u1" }); // 缺 username
|
||||||
|
console.assert(false, "应该抛错");
|
||||||
|
} catch (e) {
|
||||||
|
console.assert(e instanceof z.ZodError);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## [Implementation Order]
|
||||||
|
|
||||||
|
按依赖关系倒序执行,**5 个批次**:
|
||||||
|
|
||||||
|
1. **安装依赖**:`pnpm add zod`(修改 `package.json` + `pnpm-lock.yaml`)
|
||||||
|
2. **批次 A:`user/` (8 文件)**
|
||||||
|
- `PersonalityTraits`(最底层,被 `User` 引用)
|
||||||
|
- `User`(引用 `PersonalityTraits`)
|
||||||
|
- `RecentMemory`(被 `UserStatsResponse` 引用)
|
||||||
|
- `UserStatsResponse`(引用 `PersonalityTraits` + `RecentMemory`)
|
||||||
|
- `CreditsData`、`CreditsHistoryData`、`AvatarData`、`UpdateProfileRequest`
|
||||||
|
3. **批次 B:`auth/` 请求 (9 文件)** — 独立
|
||||||
|
4. **批次 C:`auth/` 响应 (4 文件)** — 依赖 `User`
|
||||||
|
5. **批次 D:`chat/` (10 文件)** — 内部 `ChatMessage` 引用
|
||||||
|
6. **批次 E:`metrics/` (2 文件)** — 独立
|
||||||
|
7. **最终验证**:`tsc --noEmit` + `lint` + 端到端测试
|
||||||
+3
-2
@@ -7,13 +7,14 @@
|
|||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "eslint",
|
"lint": "eslint",
|
||||||
"lint:fix": "eslint --fix",
|
"lint:fix": "eslint --fix",
|
||||||
"generate-barrels": "barrelsby --delete"
|
"generate-barrels": "barrelsby --delete"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"next": "16.2.7",
|
"next": "16.2.7",
|
||||||
"react": "19.2.4",
|
"react": "19.2.4",
|
||||||
"react-dom": "19.2.4"
|
"react-dom": "19.2.4",
|
||||||
|
"zod": "^4.4.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/postcss": "^4",
|
"@tailwindcss/postcss": "^4",
|
||||||
|
|||||||
Generated
+3
@@ -17,6 +17,9 @@ importers:
|
|||||||
react-dom:
|
react-dom:
|
||||||
specifier: 19.2.4
|
specifier: 19.2.4
|
||||||
version: 19.2.4(react@19.2.4)
|
version: 19.2.4(react@19.2.4)
|
||||||
|
zod:
|
||||||
|
specifier: ^4.4.3
|
||||||
|
version: 4.4.3
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@tailwindcss/postcss':
|
'@tailwindcss/postcss':
|
||||||
specifier: ^4
|
specifier: ^4
|
||||||
|
|||||||
@@ -2,36 +2,35 @@
|
|||||||
* Apple 登录请求
|
* Apple 登录请求
|
||||||
* 原始 Dart: AppleLoginRequest (lib/data/models/auth/auth_request.dart)
|
* 原始 Dart: AppleLoginRequest (lib/data/models/auth/auth_request.dart)
|
||||||
*/
|
*/
|
||||||
export class AppleLoginRequest {
|
import { z } from "zod";
|
||||||
readonly identityToken: string;
|
|
||||||
readonly platform: string;
|
|
||||||
|
|
||||||
constructor(params: { identityToken: string; platform: string }) {
|
export const AppleLoginRequestSchema = z.object({
|
||||||
this.identityToken = params.identityToken;
|
identityToken: z.string(),
|
||||||
this.platform = params.platform;
|
platform: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type AppleLoginRequestInput = z.input<typeof AppleLoginRequestSchema>;
|
||||||
|
export type AppleLoginRequestData = z.output<typeof AppleLoginRequestSchema>;
|
||||||
|
|
||||||
|
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);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: AppleLoginRequestInput): AppleLoginRequest {
|
||||||
return {
|
return new AppleLoginRequest(input);
|
||||||
identityToken: this.identityToken,
|
|
||||||
platform: this.platform,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): AppleLoginRequest {
|
static fromJson(json: unknown): AppleLoginRequest {
|
||||||
const requireString = (key: string): string => {
|
return AppleLoginRequest.from(json as AppleLoginRequestInput);
|
||||||
const v = json[key];
|
}
|
||||||
if (typeof v !== "string") {
|
|
||||||
throw new Error(
|
toJson(): AppleLoginRequestData {
|
||||||
`AppleLoginRequest.${key} is required and must be a string`
|
return AppleLoginRequestSchema.parse(this);
|
||||||
);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
};
|
|
||||||
return new AppleLoginRequest({
|
|
||||||
identityToken: requireString("identityToken"),
|
|
||||||
platform: requireString("platform"),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +1,38 @@
|
|||||||
/**
|
/**
|
||||||
* Facebook 登录请求
|
* Facebook 登录请求
|
||||||
* 原始 Dart: FacebookLoginRequest (lib/data/models/auth/auth_request.dart)
|
* 原始 Dart: FacebookLoginRequest (lib/data/models/auth/auth_request.dart)
|
||||||
*
|
|
||||||
* 注:原始 Dart 中 `guestId` 为 `String?`,按"全部非空"约定统一兜底为空串。
|
|
||||||
*/
|
*/
|
||||||
export class FacebookLoginRequest {
|
import { z } from "zod";
|
||||||
readonly accessToken: string;
|
|
||||||
readonly platform: string;
|
|
||||||
readonly guestId: string;
|
|
||||||
|
|
||||||
constructor(params: {
|
export const FacebookLoginRequestSchema = z.object({
|
||||||
accessToken: string;
|
accessToken: z.string(),
|
||||||
platform: string;
|
platform: z.string(),
|
||||||
guestId?: string;
|
guestId: z.string().default(""),
|
||||||
}) {
|
});
|
||||||
this.accessToken = params.accessToken;
|
|
||||||
this.platform = params.platform;
|
export type FacebookLoginRequestInput = z.input<typeof FacebookLoginRequestSchema>;
|
||||||
this.guestId = params.guestId ?? "";
|
export type FacebookLoginRequestData = z.output<typeof FacebookLoginRequestSchema>;
|
||||||
|
|
||||||
|
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);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: FacebookLoginRequestInput): FacebookLoginRequest {
|
||||||
return {
|
return new FacebookLoginRequest(input);
|
||||||
accessToken: this.accessToken,
|
|
||||||
platform: this.platform,
|
|
||||||
guestId: this.guestId,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): FacebookLoginRequest {
|
static fromJson(json: unknown): FacebookLoginRequest {
|
||||||
const requireString = (key: string): string => {
|
return FacebookLoginRequest.from(json as FacebookLoginRequestInput);
|
||||||
const v = json[key];
|
}
|
||||||
if (typeof v !== "string") {
|
|
||||||
throw new Error(
|
toJson(): FacebookLoginRequestData {
|
||||||
`FacebookLoginRequest.${key} is required and must be a string`
|
return FacebookLoginRequestSchema.parse(this);
|
||||||
);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
};
|
|
||||||
return new FacebookLoginRequest({
|
|
||||||
accessToken: requireString("accessToken"),
|
|
||||||
platform: requireString("platform"),
|
|
||||||
guestId: typeof json.guestId === "string" ? json.guestId : undefined,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,36 @@
|
|||||||
/**
|
/**
|
||||||
* Facebook ID 登录请求
|
* Facebook ID 登录请求
|
||||||
* 原始 Dart: FbIdLoginRequest (lib/data/models/auth/auth_request.dart)
|
* 原始 Dart: FbIdLoginRequest (lib/data/models/auth/auth_request.dart)
|
||||||
*
|
|
||||||
* 注:原始 Dart 中 `avatarUrl` 为 `String?`,按"全部非空"约定统一兜底为空串。
|
|
||||||
*/
|
*/
|
||||||
export class FbIdLoginRequest {
|
import { z } from "zod";
|
||||||
readonly fbId: string;
|
|
||||||
readonly avatarUrl: string;
|
|
||||||
|
|
||||||
constructor(params: { fbId: string; avatarUrl?: string }) {
|
export const FbIdLoginRequestSchema = z.object({
|
||||||
this.fbId = params.fbId;
|
fbId: z.string(),
|
||||||
this.avatarUrl = params.avatarUrl ?? "";
|
avatarUrl: z.string().default(""),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type FbIdLoginRequestInput = z.input<typeof FbIdLoginRequestSchema>;
|
||||||
|
export type FbIdLoginRequestData = z.output<typeof FbIdLoginRequestSchema>;
|
||||||
|
|
||||||
|
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);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: FbIdLoginRequestInput): FbIdLoginRequest {
|
||||||
return {
|
return new FbIdLoginRequest(input);
|
||||||
fbId: this.fbId,
|
|
||||||
avatarUrl: this.avatarUrl,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): FbIdLoginRequest {
|
static fromJson(json: unknown): FbIdLoginRequest {
|
||||||
const fbId = json.fbId;
|
return FbIdLoginRequest.from(json as FbIdLoginRequestInput);
|
||||||
if (typeof fbId !== "string") {
|
}
|
||||||
throw new Error(
|
|
||||||
"FbIdLoginRequest.fbId is required and must be a string"
|
toJson(): FbIdLoginRequestData {
|
||||||
);
|
return FbIdLoginRequestSchema.parse(this);
|
||||||
}
|
|
||||||
return new FbIdLoginRequest({
|
|
||||||
fbId,
|
|
||||||
avatarUrl:
|
|
||||||
typeof json.avatarUrl === "string" ? json.avatarUrl : undefined,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +1,38 @@
|
|||||||
/**
|
/**
|
||||||
* Google 登录请求
|
* Google 登录请求
|
||||||
* 原始 Dart: GoogleLoginRequest (lib/data/models/auth/auth_request.dart)
|
* 原始 Dart: GoogleLoginRequest (lib/data/models/auth/auth_request.dart)
|
||||||
*
|
|
||||||
* 注:原始 Dart 中 `guestId` 为 `String?`,按"全部非空"约定统一兜底为空串。
|
|
||||||
*/
|
*/
|
||||||
export class GoogleLoginRequest {
|
import { z } from "zod";
|
||||||
readonly idToken: string;
|
|
||||||
readonly platform: string;
|
|
||||||
readonly guestId: string;
|
|
||||||
|
|
||||||
constructor(params: {
|
export const GoogleLoginRequestSchema = z.object({
|
||||||
idToken: string;
|
idToken: z.string(),
|
||||||
platform: string;
|
platform: z.string(),
|
||||||
guestId?: string;
|
guestId: z.string().default(""),
|
||||||
}) {
|
});
|
||||||
this.idToken = params.idToken;
|
|
||||||
this.platform = params.platform;
|
export type GoogleLoginRequestInput = z.input<typeof GoogleLoginRequestSchema>;
|
||||||
this.guestId = params.guestId ?? "";
|
export type GoogleLoginRequestData = z.output<typeof GoogleLoginRequestSchema>;
|
||||||
|
|
||||||
|
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);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: GoogleLoginRequestInput): GoogleLoginRequest {
|
||||||
return {
|
return new GoogleLoginRequest(input);
|
||||||
idToken: this.idToken,
|
|
||||||
platform: this.platform,
|
|
||||||
guestId: this.guestId,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): GoogleLoginRequest {
|
static fromJson(json: unknown): GoogleLoginRequest {
|
||||||
const requireString = (key: string): string => {
|
return GoogleLoginRequest.from(json as GoogleLoginRequestInput);
|
||||||
const v = json[key];
|
}
|
||||||
if (typeof v !== "string") {
|
|
||||||
throw new Error(
|
toJson(): GoogleLoginRequestData {
|
||||||
`GoogleLoginRequest.${key} is required and must be a string`
|
return GoogleLoginRequestSchema.parse(this);
|
||||||
);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
};
|
|
||||||
return new GoogleLoginRequest({
|
|
||||||
idToken: requireString("idToken"),
|
|
||||||
platform: requireString("platform"),
|
|
||||||
guestId: typeof json.guestId === "string" ? json.guestId : undefined,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,25 +2,33 @@
|
|||||||
* 游客登录请求
|
* 游客登录请求
|
||||||
* 原始 Dart: GuestLoginRequest (lib/data/models/auth/auth_request.dart)
|
* 原始 Dart: GuestLoginRequest (lib/data/models/auth/auth_request.dart)
|
||||||
*/
|
*/
|
||||||
export class GuestLoginRequest {
|
import { z } from "zod";
|
||||||
readonly deviceId: string;
|
|
||||||
|
|
||||||
constructor(params: { deviceId: string }) {
|
export const GuestLoginRequestSchema = z.object({
|
||||||
this.deviceId = params.deviceId;
|
deviceId: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type GuestLoginRequestInput = z.input<typeof GuestLoginRequestSchema>;
|
||||||
|
export type GuestLoginRequestData = z.output<typeof GuestLoginRequestSchema>;
|
||||||
|
|
||||||
|
export class GuestLoginRequest {
|
||||||
|
declare readonly deviceId: string;
|
||||||
|
|
||||||
|
private constructor(input: GuestLoginRequestInput) {
|
||||||
|
const data = GuestLoginRequestSchema.parse(input);
|
||||||
|
Object.assign(this, data);
|
||||||
Object.freeze(this);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: GuestLoginRequestInput): GuestLoginRequest {
|
||||||
return { deviceId: this.deviceId };
|
return new GuestLoginRequest(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): GuestLoginRequest {
|
static fromJson(json: unknown): GuestLoginRequest {
|
||||||
const deviceId = json.deviceId;
|
return GuestLoginRequest.from(json as GuestLoginRequestInput);
|
||||||
if (typeof deviceId !== "string") {
|
}
|
||||||
throw new Error(
|
|
||||||
"GuestLoginRequest.deviceId is required and must be a string"
|
toJson(): GuestLoginRequestData {
|
||||||
);
|
return GuestLoginRequestSchema.parse(this);
|
||||||
}
|
|
||||||
return new GuestLoginRequest({ deviceId });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,73 +1,52 @@
|
|||||||
/**
|
/**
|
||||||
* 游客登录响应
|
* 游客登录响应
|
||||||
* 原始 Dart: GuestLoginResponse (lib/data/models/auth/auth_response.dart)
|
* 原始 Dart: GuestLoginResponse (lib/data/models/auth/auth_response.dart)
|
||||||
*
|
|
||||||
* 注:原始 Dart 中 `user` 为 `User?`,按"全部非空"约定兜底为默认 User 实例
|
|
||||||
* (id/username 为空串,使用方可通过 user.id === "" 判定无用户数据)。
|
|
||||||
*/
|
*/
|
||||||
import { User } from "../user/user";
|
import { z } from "zod";
|
||||||
|
import { User, UserSchema } from "../user/user";
|
||||||
|
|
||||||
|
export const GuestLoginResponseSchema = z.object({
|
||||||
|
token: z.string(),
|
||||||
|
deviceId: z.string(),
|
||||||
|
userId: z.string(),
|
||||||
|
isDeviceUser: z.boolean().default(true),
|
||||||
|
expiresIn: z.number().default(2592000),
|
||||||
|
user: UserSchema.optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type GuestLoginResponseInput = z.input<typeof GuestLoginResponseSchema>;
|
||||||
|
export type GuestLoginResponseData = z.output<typeof GuestLoginResponseSchema>;
|
||||||
|
|
||||||
export class GuestLoginResponse {
|
export class GuestLoginResponse {
|
||||||
readonly token: string;
|
declare readonly token: string;
|
||||||
readonly deviceId: string;
|
declare readonly deviceId: string;
|
||||||
readonly userId: string;
|
declare readonly userId: string;
|
||||||
readonly isDeviceUser: boolean;
|
declare readonly isDeviceUser: boolean;
|
||||||
readonly expiresIn: number;
|
declare readonly expiresIn: number;
|
||||||
readonly user: User;
|
declare readonly user: User;
|
||||||
|
|
||||||
constructor(params: {
|
private constructor(data: GuestLoginResponseData) {
|
||||||
token: string;
|
Object.assign(this, data);
|
||||||
deviceId: string;
|
|
||||||
userId: string;
|
|
||||||
isDeviceUser?: boolean;
|
|
||||||
expiresIn?: number;
|
|
||||||
user?: User;
|
|
||||||
}) {
|
|
||||||
this.token = params.token;
|
|
||||||
this.deviceId = params.deviceId;
|
|
||||||
this.userId = params.userId;
|
|
||||||
this.isDeviceUser = params.isDeviceUser ?? true;
|
|
||||||
this.expiresIn = params.expiresIn ?? 2592000;
|
|
||||||
this.user = params.user ?? new User({ id: "", username: "" });
|
|
||||||
Object.freeze(this);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: GuestLoginResponseInput): GuestLoginResponse {
|
||||||
return {
|
const parsed = GuestLoginResponseSchema.parse(input);
|
||||||
token: this.token,
|
return new GuestLoginResponse({
|
||||||
deviceId: this.deviceId,
|
...parsed,
|
||||||
userId: this.userId,
|
user: parsed.user ?? User.empty(),
|
||||||
isDeviceUser: this.isDeviceUser,
|
});
|
||||||
expiresIn: this.expiresIn,
|
|
||||||
user: this.user.toJson(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): GuestLoginResponse {
|
static fromJson(json: unknown): GuestLoginResponse {
|
||||||
const requireString = (key: string): string => {
|
return GuestLoginResponse.from(json as GuestLoginResponseInput);
|
||||||
const v = json[key];
|
}
|
||||||
if (typeof v !== "string") {
|
|
||||||
throw new Error(
|
toJson(): GuestLoginResponseData {
|
||||||
`GuestLoginResponse.${key} is required and must be a string`
|
const { user, ...rest } = this;
|
||||||
);
|
return GuestLoginResponseSchema.parse({
|
||||||
}
|
...rest,
|
||||||
return v;
|
...(user.id !== "" ? { user } : {}),
|
||||||
};
|
|
||||||
const userJson = json.user;
|
|
||||||
const user: User =
|
|
||||||
typeof userJson === "object" && userJson !== null
|
|
||||||
? User.fromJson(userJson as Record<string, unknown>)
|
|
||||||
: new User({ id: "", username: "" });
|
|
||||||
return new GuestLoginResponse({
|
|
||||||
token: requireString("token"),
|
|
||||||
deviceId: requireString("deviceId"),
|
|
||||||
userId: requireString("userId"),
|
|
||||||
isDeviceUser:
|
|
||||||
typeof json.isDeviceUser === "boolean"
|
|
||||||
? json.isDeviceUser
|
|
||||||
: undefined,
|
|
||||||
expiresIn: typeof json.expiresIn === "number" ? json.expiresIn : undefined,
|
|
||||||
user,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,58 +1,42 @@
|
|||||||
/**
|
/**
|
||||||
* 登录请求 - 支持用户名或邮箱登录
|
* 登录请求 - 支持用户名或邮箱登录
|
||||||
* 原始 Dart: LoginRequest (lib/data/models/auth/auth_request.dart)
|
* 原始 Dart: LoginRequest (lib/data/models/auth/auth_request.dart)
|
||||||
*
|
|
||||||
* 注:原始 Dart 中 `email`/`username`/`guestId` 为 `String?`,按"全部非空"约定统一兜底为空串。
|
|
||||||
* 后端校验 email 与 username 至少传入一个。
|
|
||||||
*/
|
*/
|
||||||
export class LoginRequest {
|
import { z } from "zod";
|
||||||
readonly email: string;
|
|
||||||
readonly username: string;
|
|
||||||
readonly password: string;
|
|
||||||
readonly platform: string;
|
|
||||||
readonly guestId: string;
|
|
||||||
|
|
||||||
constructor(params: {
|
export const LoginRequestSchema = z.object({
|
||||||
email?: string;
|
email: z.string().default(""),
|
||||||
username?: string;
|
username: z.string().default(""),
|
||||||
password: string;
|
password: z.string(),
|
||||||
platform: string;
|
platform: z.string(),
|
||||||
guestId?: string;
|
guestId: z.string().default(""),
|
||||||
}) {
|
});
|
||||||
this.email = params.email ?? "";
|
|
||||||
this.username = params.username ?? "";
|
export type LoginRequestInput = z.input<typeof LoginRequestSchema>;
|
||||||
this.password = params.password;
|
export type LoginRequestData = z.output<typeof LoginRequestSchema>;
|
||||||
this.platform = params.platform;
|
|
||||||
this.guestId = params.guestId ?? "";
|
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);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: LoginRequestInput): LoginRequest {
|
||||||
return {
|
return new LoginRequest(input);
|
||||||
email: this.email,
|
|
||||||
username: this.username,
|
|
||||||
password: this.password,
|
|
||||||
platform: this.platform,
|
|
||||||
guestId: this.guestId,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): LoginRequest {
|
static fromJson(json: unknown): LoginRequest {
|
||||||
const requireString = (key: string): string => {
|
return LoginRequest.from(json as LoginRequestInput);
|
||||||
const v = json[key];
|
}
|
||||||
if (typeof v !== "string") {
|
|
||||||
throw new Error(
|
toJson(): LoginRequestData {
|
||||||
`LoginRequest.${key} is required and must be a string`
|
return LoginRequestSchema.parse(this);
|
||||||
);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
};
|
|
||||||
return new LoginRequest({
|
|
||||||
email: typeof json.email === "string" ? json.email : undefined,
|
|
||||||
username: typeof json.username === "string" ? json.username : undefined,
|
|
||||||
password: requireString("password"),
|
|
||||||
platform: requireString("platform"),
|
|
||||||
guestId: typeof json.guestId === "string" ? json.guestId : undefined,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,42 +2,38 @@
|
|||||||
* 登录响应
|
* 登录响应
|
||||||
* 原始 Dart: LoginResponse (lib/data/models/auth/auth_response.dart)
|
* 原始 Dart: LoginResponse (lib/data/models/auth/auth_response.dart)
|
||||||
*/
|
*/
|
||||||
import { User } from "../user/user";
|
import { z } from "zod";
|
||||||
|
import { User, UserSchema } from "../user/user";
|
||||||
|
|
||||||
|
export const LoginResponseSchema = z.object({
|
||||||
|
user: UserSchema,
|
||||||
|
token: z.string(),
|
||||||
|
refreshToken: z.string().default(""),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type LoginResponseInput = z.input<typeof LoginResponseSchema>;
|
||||||
|
export type LoginResponseData = z.output<typeof LoginResponseSchema>;
|
||||||
|
|
||||||
export class LoginResponse {
|
export class LoginResponse {
|
||||||
readonly user: User;
|
declare readonly user: User;
|
||||||
readonly token: string;
|
declare readonly token: string;
|
||||||
readonly refreshToken: string;
|
declare readonly refreshToken: string;
|
||||||
|
|
||||||
constructor(params: { user: User; token: string; refreshToken?: string }) {
|
private constructor(input: LoginResponseInput) {
|
||||||
this.user = params.user;
|
const data = LoginResponseSchema.parse(input);
|
||||||
this.token = params.token;
|
Object.assign(this, data);
|
||||||
this.refreshToken = params.refreshToken ?? "";
|
|
||||||
Object.freeze(this);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: LoginResponseInput): LoginResponse {
|
||||||
return {
|
return new LoginResponse(input);
|
||||||
user: this.user.toJson(),
|
|
||||||
token: this.token,
|
|
||||||
refreshToken: this.refreshToken,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): LoginResponse {
|
static fromJson(json: unknown): LoginResponse {
|
||||||
const token = json.token;
|
return LoginResponse.from(json as LoginResponseInput);
|
||||||
if (typeof token !== "string") {
|
}
|
||||||
throw new Error("LoginResponse.token is required and must be a string");
|
|
||||||
}
|
toJson(): LoginResponseData {
|
||||||
const userJson = json.user;
|
return LoginResponseSchema.parse(this);
|
||||||
if (typeof userJson !== "object" || userJson === null) {
|
|
||||||
throw new Error("LoginResponse.user is required and must be an object");
|
|
||||||
}
|
|
||||||
return new LoginResponse({
|
|
||||||
user: User.fromJson(userJson as Record<string, unknown>),
|
|
||||||
token,
|
|
||||||
refreshToken:
|
|
||||||
typeof json.refreshToken === "string" ? json.refreshToken : undefined,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,21 +2,33 @@
|
|||||||
* 退出登录响应
|
* 退出登录响应
|
||||||
* 原始 Dart: LogoutResponse (lib/data/models/auth/auth_response.dart)
|
* 原始 Dart: LogoutResponse (lib/data/models/auth/auth_response.dart)
|
||||||
*/
|
*/
|
||||||
export class LogoutResponse {
|
import { z } from "zod";
|
||||||
readonly success: boolean;
|
|
||||||
|
|
||||||
constructor(params: { success?: boolean }) {
|
export const LogoutResponseSchema = z.object({
|
||||||
this.success = params.success ?? true;
|
success: z.boolean().default(true),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type LogoutResponseInput = z.input<typeof LogoutResponseSchema>;
|
||||||
|
export type LogoutResponseData = z.output<typeof LogoutResponseSchema>;
|
||||||
|
|
||||||
|
export class LogoutResponse {
|
||||||
|
declare readonly success: boolean;
|
||||||
|
|
||||||
|
private constructor(input: LogoutResponseInput) {
|
||||||
|
const data = LogoutResponseSchema.parse(input);
|
||||||
|
Object.assign(this, data);
|
||||||
Object.freeze(this);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: LogoutResponseInput): LogoutResponse {
|
||||||
return { success: this.success };
|
return new LogoutResponse(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): LogoutResponse {
|
static fromJson(json: unknown): LogoutResponse {
|
||||||
return new LogoutResponse({
|
return LogoutResponse.from(json as LogoutResponseInput);
|
||||||
success: typeof json.success === "boolean" ? json.success : undefined,
|
}
|
||||||
});
|
|
||||||
|
toJson(): LogoutResponseData {
|
||||||
|
return LogoutResponseSchema.parse(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,25 +2,33 @@
|
|||||||
* 刷新 Token 请求
|
* 刷新 Token 请求
|
||||||
* 原始 Dart: RefreshTokenRequest (lib/data/models/auth/auth_request.dart)
|
* 原始 Dart: RefreshTokenRequest (lib/data/models/auth/auth_request.dart)
|
||||||
*/
|
*/
|
||||||
export class RefreshTokenRequest {
|
import { z } from "zod";
|
||||||
readonly refreshToken: string;
|
|
||||||
|
|
||||||
constructor(params: { refreshToken: string }) {
|
export const RefreshTokenRequestSchema = z.object({
|
||||||
this.refreshToken = params.refreshToken;
|
refreshToken: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type RefreshTokenRequestInput = z.input<typeof RefreshTokenRequestSchema>;
|
||||||
|
export type RefreshTokenRequestData = z.output<typeof RefreshTokenRequestSchema>;
|
||||||
|
|
||||||
|
export class RefreshTokenRequest {
|
||||||
|
declare readonly refreshToken: string;
|
||||||
|
|
||||||
|
private constructor(input: RefreshTokenRequestInput) {
|
||||||
|
const data = RefreshTokenRequestSchema.parse(input);
|
||||||
|
Object.assign(this, data);
|
||||||
Object.freeze(this);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: RefreshTokenRequestInput): RefreshTokenRequest {
|
||||||
return { refreshToken: this.refreshToken };
|
return new RefreshTokenRequest(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): RefreshTokenRequest {
|
static fromJson(json: unknown): RefreshTokenRequest {
|
||||||
const refreshToken = json.refreshToken;
|
return RefreshTokenRequest.from(json as RefreshTokenRequestInput);
|
||||||
if (typeof refreshToken !== "string") {
|
}
|
||||||
throw new Error(
|
|
||||||
"RefreshTokenRequest.refreshToken is required and must be a string"
|
toJson(): RefreshTokenRequestData {
|
||||||
);
|
return RefreshTokenRequestSchema.parse(this);
|
||||||
}
|
|
||||||
return new RefreshTokenRequest({ refreshToken });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,44 +2,37 @@
|
|||||||
* 刷新 Token 响应
|
* 刷新 Token 响应
|
||||||
* 原始 Dart: RefreshTokenResponse (lib/data/models/auth/auth_response.dart)
|
* 原始 Dart: RefreshTokenResponse (lib/data/models/auth/auth_response.dart)
|
||||||
*/
|
*/
|
||||||
export class RefreshTokenResponse {
|
import { z } from "zod";
|
||||||
readonly token: string;
|
|
||||||
readonly refreshToken: string;
|
|
||||||
readonly userId: string;
|
|
||||||
|
|
||||||
constructor(params: {
|
export const RefreshTokenResponseSchema = z.object({
|
||||||
token: string;
|
token: z.string(),
|
||||||
refreshToken: string;
|
refreshToken: z.string(),
|
||||||
userId: string;
|
userId: z.string(),
|
||||||
}) {
|
});
|
||||||
this.token = params.token;
|
|
||||||
this.refreshToken = params.refreshToken;
|
export type RefreshTokenResponseInput = z.input<typeof RefreshTokenResponseSchema>;
|
||||||
this.userId = params.userId;
|
export type RefreshTokenResponseData = z.output<typeof RefreshTokenResponseSchema>;
|
||||||
|
|
||||||
|
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);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: RefreshTokenResponseInput): RefreshTokenResponse {
|
||||||
return {
|
return new RefreshTokenResponse(input);
|
||||||
token: this.token,
|
|
||||||
refreshToken: this.refreshToken,
|
|
||||||
userId: this.userId,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): RefreshTokenResponse {
|
static fromJson(json: unknown): RefreshTokenResponse {
|
||||||
const requireString = (key: string): string => {
|
return RefreshTokenResponse.from(json as RefreshTokenResponseInput);
|
||||||
const v = json[key];
|
}
|
||||||
if (typeof v !== "string") {
|
|
||||||
throw new Error(
|
toJson(): RefreshTokenResponseData {
|
||||||
`RefreshTokenResponse.${key} is required and must be a string`
|
return RefreshTokenResponseSchema.parse(this);
|
||||||
);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
};
|
|
||||||
return new RefreshTokenResponse({
|
|
||||||
token: requireString("token"),
|
|
||||||
refreshToken: requireString("refreshToken"),
|
|
||||||
userId: requireString("userId"),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +1,42 @@
|
|||||||
/**
|
/**
|
||||||
* 注册请求
|
* 注册请求
|
||||||
* 原始 Dart: RegisterRequest (lib/data/models/auth/auth_request.dart)
|
* 原始 Dart: RegisterRequest (lib/data/models/auth/auth_request.dart)
|
||||||
*
|
|
||||||
* 注:原始 Dart 中 `guestId` 为 `String?`,按"全部非空"约定统一兜底为空串。
|
|
||||||
*/
|
*/
|
||||||
export class RegisterRequest {
|
import { z } from "zod";
|
||||||
readonly username: string;
|
|
||||||
readonly email: string;
|
|
||||||
readonly password: string;
|
|
||||||
readonly platform: string;
|
|
||||||
readonly guestId: string;
|
|
||||||
|
|
||||||
constructor(params: {
|
export const RegisterRequestSchema = z.object({
|
||||||
username: string;
|
username: z.string(),
|
||||||
email: string;
|
email: z.string(),
|
||||||
password: string;
|
password: z.string(),
|
||||||
platform: string;
|
platform: z.string(),
|
||||||
guestId?: string;
|
guestId: z.string().default(""),
|
||||||
}) {
|
});
|
||||||
this.username = params.username;
|
|
||||||
this.email = params.email;
|
export type RegisterRequestInput = z.input<typeof RegisterRequestSchema>;
|
||||||
this.password = params.password;
|
export type RegisterRequestData = z.output<typeof RegisterRequestSchema>;
|
||||||
this.platform = params.platform;
|
|
||||||
this.guestId = params.guestId ?? "";
|
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);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: RegisterRequestInput): RegisterRequest {
|
||||||
return {
|
return new RegisterRequest(input);
|
||||||
username: this.username,
|
|
||||||
email: this.email,
|
|
||||||
password: this.password,
|
|
||||||
platform: this.platform,
|
|
||||||
guestId: this.guestId,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): RegisterRequest {
|
static fromJson(json: unknown): RegisterRequest {
|
||||||
const requireString = (key: string): string => {
|
return RegisterRequest.from(json as RegisterRequestInput);
|
||||||
const v = json[key];
|
}
|
||||||
if (typeof v !== "string") {
|
|
||||||
throw new Error(
|
toJson(): RegisterRequestData {
|
||||||
`RegisterRequest.${key} is required and must be a string`
|
return RegisterRequestSchema.parse(this);
|
||||||
);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
};
|
|
||||||
return new RegisterRequest({
|
|
||||||
username: requireString("username"),
|
|
||||||
email: requireString("email"),
|
|
||||||
password: requireString("password"),
|
|
||||||
platform: requireString("platform"),
|
|
||||||
guestId: typeof json.guestId === "string" ? json.guestId : undefined,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,25 +2,33 @@
|
|||||||
* 发送验证码请求
|
* 发送验证码请求
|
||||||
* 原始 Dart: SendCodeRequest (lib/data/models/auth/auth_request.dart)
|
* 原始 Dart: SendCodeRequest (lib/data/models/auth/auth_request.dart)
|
||||||
*/
|
*/
|
||||||
export class SendCodeRequest {
|
import { z } from "zod";
|
||||||
readonly email: string;
|
|
||||||
|
|
||||||
constructor(params: { email: string }) {
|
export const SendCodeRequestSchema = z.object({
|
||||||
this.email = params.email;
|
email: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type SendCodeRequestInput = z.input<typeof SendCodeRequestSchema>;
|
||||||
|
export type SendCodeRequestData = z.output<typeof SendCodeRequestSchema>;
|
||||||
|
|
||||||
|
export class SendCodeRequest {
|
||||||
|
declare readonly email: string;
|
||||||
|
|
||||||
|
private constructor(input: SendCodeRequestInput) {
|
||||||
|
const data = SendCodeRequestSchema.parse(input);
|
||||||
|
Object.assign(this, data);
|
||||||
Object.freeze(this);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: SendCodeRequestInput): SendCodeRequest {
|
||||||
return { email: this.email };
|
return new SendCodeRequest(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): SendCodeRequest {
|
static fromJson(json: unknown): SendCodeRequest {
|
||||||
const email = json.email;
|
return SendCodeRequest.from(json as SendCodeRequestInput);
|
||||||
if (typeof email !== "string") {
|
}
|
||||||
throw new Error(
|
|
||||||
"SendCodeRequest.email is required and must be a string"
|
toJson(): SendCodeRequestData {
|
||||||
);
|
return SendCodeRequestSchema.parse(this);
|
||||||
}
|
|
||||||
return new SendCodeRequest({ email });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,60 +1,41 @@
|
|||||||
/**
|
/**
|
||||||
* 聊天历史响应
|
* 聊天历史响应
|
||||||
* 原始 Dart: ChatHistoryResponse (lib/data/models/chat/chat_response.dart)
|
* 原始 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 {
|
export class ChatHistoryResponse {
|
||||||
readonly messages: ChatMessage[];
|
declare readonly messages: ChatMessage[];
|
||||||
readonly total: number;
|
declare readonly total: number;
|
||||||
readonly limit: number;
|
declare readonly limit: number;
|
||||||
readonly offset: number;
|
declare readonly offset: number;
|
||||||
|
|
||||||
constructor(params: {
|
private constructor(input: ChatHistoryResponseInput) {
|
||||||
messages: ChatMessage[];
|
const data = ChatHistoryResponseSchema.parse(input);
|
||||||
total?: number;
|
Object.assign(this, data);
|
||||||
limit: number;
|
|
||||||
offset: number;
|
|
||||||
}) {
|
|
||||||
this.messages = params.messages;
|
|
||||||
this.total = params.total ?? 0;
|
|
||||||
this.limit = params.limit;
|
|
||||||
this.offset = params.offset;
|
|
||||||
Object.freeze(this);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: ChatHistoryResponseInput): ChatHistoryResponse {
|
||||||
return {
|
return new ChatHistoryResponse(input);
|
||||||
messages: this.messages.map((m) => m.toJson()),
|
|
||||||
total: this.total,
|
|
||||||
limit: this.limit,
|
|
||||||
offset: this.offset,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): ChatHistoryResponse {
|
static fromJson(json: unknown): ChatHistoryResponse {
|
||||||
const requireNumber = (key: string): number => {
|
return ChatHistoryResponse.from(json as ChatHistoryResponseInput);
|
||||||
const v = json[key];
|
}
|
||||||
if (typeof v !== "number") {
|
|
||||||
throw new Error(
|
toJson(): ChatHistoryResponseData {
|
||||||
`ChatHistoryResponse.${key} is required and must be a number`
|
return ChatHistoryResponseSchema.parse(this);
|
||||||
);
|
|
||||||
}
|
|
||||||
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"),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,53 +1,40 @@
|
|||||||
/**
|
/**
|
||||||
* 聊天历史中的单条消息
|
* 聊天历史中的单条消息
|
||||||
* 原始 Dart: ChatMessage (lib/data/models/chat/chat_response.dart)
|
* 原始 Dart: ChatMessage (lib/data/models/chat/chat_response.dart)
|
||||||
*
|
|
||||||
* 注:原始 Dart 中 `id`/`createdAt` 为 `String?`,按"全部非空"约定兜底为空串。
|
|
||||||
*/
|
*/
|
||||||
export class ChatMessage {
|
import { z } from "zod";
|
||||||
readonly role: string;
|
|
||||||
readonly content: string;
|
|
||||||
readonly id: string;
|
|
||||||
readonly createdAt: string;
|
|
||||||
|
|
||||||
constructor(params: {
|
export const ChatMessageSchema = z.object({
|
||||||
role: string;
|
role: z.string(),
|
||||||
content: string;
|
content: z.string(),
|
||||||
id?: string;
|
id: z.string().default(""),
|
||||||
createdAt?: string;
|
createdAt: z.string().default(""),
|
||||||
}) {
|
});
|
||||||
this.role = params.role;
|
|
||||||
this.content = params.content;
|
export type ChatMessageInput = z.input<typeof ChatMessageSchema>;
|
||||||
this.id = params.id ?? "";
|
export type ChatMessageData = z.output<typeof ChatMessageSchema>;
|
||||||
this.createdAt = params.createdAt ?? "";
|
|
||||||
|
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);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: ChatMessageInput): ChatMessage {
|
||||||
return {
|
return new ChatMessage(input);
|
||||||
role: this.role,
|
|
||||||
content: this.content,
|
|
||||||
id: this.id,
|
|
||||||
createdAt: this.createdAt,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): ChatMessage {
|
static fromJson(json: unknown): ChatMessage {
|
||||||
const requireString = (key: string): string => {
|
return ChatMessage.from(json as ChatMessageInput);
|
||||||
const v = json[key];
|
}
|
||||||
if (typeof v !== "string") {
|
|
||||||
throw new Error(
|
toJson(): ChatMessageData {
|
||||||
`ChatMessage.${key} is required and must be a string`
|
return ChatMessageSchema.parse(this);
|
||||||
);
|
|
||||||
}
|
|
||||||
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,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,98 +1,54 @@
|
|||||||
/**
|
/**
|
||||||
* 发送消息响应
|
* 发送消息响应
|
||||||
* 原始 Dart: ChatSendResponse (lib/data/models/chat/chat_response.dart)
|
* 原始 Dart: ChatSendResponse (lib/data/models/chat/chat_response.dart)
|
||||||
*
|
|
||||||
* 注:原始 Dart 中 `mode`/`currentMood`/`isGuest` 为可空字段,按"全部非空"约定兜底:
|
|
||||||
* - 字符串 → ""
|
|
||||||
* - bool → false
|
|
||||||
* - int → 0
|
|
||||||
*/
|
*/
|
||||||
export class ChatSendResponse {
|
import { z } from "zod";
|
||||||
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;
|
|
||||||
|
|
||||||
constructor(params: {
|
export const ChatSendResponseSchema = z.object({
|
||||||
mode?: string;
|
mode: z.string().default(""),
|
||||||
reply: string;
|
reply: z.string(),
|
||||||
voiceUrl?: string;
|
voiceUrl: z.string().default(""),
|
||||||
audioUrl?: string;
|
audioUrl: z.string().default(""),
|
||||||
intimacyChange?: number;
|
intimacyChange: z.number().default(0),
|
||||||
newIntimacy?: number;
|
newIntimacy: z.number().default(0),
|
||||||
relationshipStage: string;
|
relationshipStage: z.string(),
|
||||||
currentMood?: string;
|
currentMood: z.string().default(""),
|
||||||
messageId: string;
|
messageId: z.string(),
|
||||||
isGuest?: boolean;
|
isGuest: z.boolean().default(false),
|
||||||
timestamp?: number;
|
timestamp: z.number().default(0),
|
||||||
}) {
|
});
|
||||||
this.mode = params.mode ?? "";
|
|
||||||
this.reply = params.reply;
|
export type ChatSendResponseInput = z.input<typeof ChatSendResponseSchema>;
|
||||||
this.voiceUrl = params.voiceUrl ?? "";
|
export type ChatSendResponseData = z.output<typeof ChatSendResponseSchema>;
|
||||||
this.audioUrl = params.audioUrl ?? "";
|
|
||||||
this.intimacyChange = params.intimacyChange ?? 0;
|
export class ChatSendResponse {
|
||||||
this.newIntimacy = params.newIntimacy ?? 0;
|
declare readonly mode: string;
|
||||||
this.relationshipStage = params.relationshipStage;
|
declare readonly reply: string;
|
||||||
this.currentMood = params.currentMood ?? "";
|
declare readonly voiceUrl: string;
|
||||||
this.messageId = params.messageId;
|
declare readonly audioUrl: string;
|
||||||
this.isGuest = params.isGuest ?? false;
|
declare readonly intimacyChange: number;
|
||||||
this.timestamp = params.timestamp ?? 0;
|
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);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: ChatSendResponseInput): ChatSendResponse {
|
||||||
return {
|
return new ChatSendResponse(input);
|
||||||
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 fromJson(json: Record<string, unknown>): ChatSendResponse {
|
static fromJson(json: unknown): ChatSendResponse {
|
||||||
const requireString = (key: string): string => {
|
return ChatSendResponse.from(json as ChatSendResponseInput);
|
||||||
const v = json[key];
|
}
|
||||||
if (typeof v !== "string") {
|
|
||||||
throw new Error(
|
toJson(): ChatSendResponseData {
|
||||||
`ChatSendResponse.${key} is required and must be a string`
|
return ChatSendResponseSchema.parse(this);
|
||||||
);
|
|
||||||
}
|
|
||||||
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,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,36 +2,35 @@
|
|||||||
* 消息同步响应
|
* 消息同步响应
|
||||||
* 原始 Dart: ChatSyncData (lib/data/models/chat/chat_sync.dart)
|
* 原始 Dart: ChatSyncData (lib/data/models/chat/chat_sync.dart)
|
||||||
*/
|
*/
|
||||||
export class ChatSyncData {
|
import { z } from "zod";
|
||||||
readonly syncedCount: number;
|
|
||||||
readonly totalHistory: number;
|
|
||||||
|
|
||||||
constructor(params: { syncedCount: number; totalHistory: number }) {
|
export const ChatSyncDataSchema = z.object({
|
||||||
this.syncedCount = params.syncedCount;
|
syncedCount: z.number(),
|
||||||
this.totalHistory = params.totalHistory;
|
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);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: ChatSyncDataInput): ChatSyncData {
|
||||||
return {
|
return new ChatSyncData(input);
|
||||||
syncedCount: this.syncedCount,
|
|
||||||
totalHistory: this.totalHistory,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): ChatSyncData {
|
static fromJson(json: unknown): ChatSyncData {
|
||||||
const requireNumber = (key: string): number => {
|
return ChatSyncData.from(json as ChatSyncDataInput);
|
||||||
const v = json[key];
|
}
|
||||||
if (typeof v !== "number") {
|
|
||||||
throw new Error(
|
toJson(): ChatSyncDataData {
|
||||||
`ChatSyncData.${key} is required and must be a number`
|
return ChatSyncDataSchema.parse(this);
|
||||||
);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
};
|
|
||||||
return new ChatSyncData({
|
|
||||||
syncedCount: requireNumber("syncedCount"),
|
|
||||||
totalHistory: requireNumber("totalHistory"),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,27 +2,34 @@
|
|||||||
* 消息同步请求
|
* 消息同步请求
|
||||||
* 原始 Dart: ChatSyncRequest (lib/data/models/chat/chat_sync.dart)
|
* 原始 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 {
|
export class ChatSyncRequest {
|
||||||
readonly messages: SyncMessage[];
|
declare readonly messages: SyncMessage[];
|
||||||
|
|
||||||
constructor(params: { messages: SyncMessage[] }) {
|
private constructor(input: ChatSyncRequestInput) {
|
||||||
this.messages = params.messages;
|
const data = ChatSyncRequestSchema.parse(input);
|
||||||
|
Object.assign(this, data);
|
||||||
Object.freeze(this);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: ChatSyncRequestInput): ChatSyncRequest {
|
||||||
return { messages: this.messages.map((m) => m.toJson()) };
|
return new ChatSyncRequest(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): ChatSyncRequest {
|
static fromJson(json: unknown): ChatSyncRequest {
|
||||||
const rawMessages = json.messages;
|
return ChatSyncRequest.from(json as ChatSyncRequestInput);
|
||||||
const messages: SyncMessage[] = Array.isArray(rawMessages)
|
}
|
||||||
? (rawMessages as Record<string, unknown>[]).map((m) =>
|
|
||||||
SyncMessage.fromJson(m)
|
toJson(): ChatSyncRequestData {
|
||||||
)
|
return ChatSyncRequestSchema.parse(this);
|
||||||
: [];
|
|
||||||
return new ChatSyncRequest({ messages });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,17 +2,23 @@
|
|||||||
* 游客每日聊天配额数据
|
* 游客每日聊天配额数据
|
||||||
* 原始 Dart: GuestChatQuota (lib/data/models/chat/guest_chat_quota.dart)
|
* 原始 Dart: GuestChatQuota (lib/data/models/chat/guest_chat_quota.dart)
|
||||||
*
|
*
|
||||||
* 注:原始 Dart 中的静态常量与运行时环境判断方法(isDevelopment)已迁移为
|
* 注:保留原始 Dart 中的静态常量与运行时环境判断方法。
|
||||||
* 静态成员。环境检测逻辑(threshold / totalQuotaDefault 等)保留为方法,
|
* `process.env.NODE_ENV` 替代 Dart 的 `AppEnvUtil.isDevelopment`。
|
||||||
* 内部使用 `process.env.NODE_ENV` 替代 Dart 中的 `AppEnvUtil.isDevelopment`。
|
* `initial()` 工厂方法与 `needReset` 实例方法保留。
|
||||||
* `initial()` 工厂方法已迁移为静态方法;`needReset` getter 保留为实例方法。
|
|
||||||
*
|
|
||||||
* Dart 中 `initial()` 依赖 `app_date.DateUtils.todayString()`,
|
|
||||||
* 在 TS 中由调用方通过 `date` 字段传入。
|
|
||||||
*/
|
*/
|
||||||
|
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 {
|
export class GuestChatQuota {
|
||||||
readonly remaining: number;
|
declare readonly remaining: number;
|
||||||
readonly date: string;
|
declare readonly date: string;
|
||||||
|
|
||||||
// 静态常量
|
// 静态常量
|
||||||
static readonly maxQuotaPerDay = 40;
|
static readonly maxQuotaPerDay = 40;
|
||||||
@@ -23,12 +29,24 @@ export class GuestChatQuota {
|
|||||||
static readonly defaultTotalQuotaDev = 5;
|
static readonly defaultTotalQuotaDev = 5;
|
||||||
static readonly quotaExhausted = 0;
|
static readonly quotaExhausted = 0;
|
||||||
|
|
||||||
constructor(params: { remaining: number; date: string }) {
|
private constructor(input: GuestChatQuotaInput) {
|
||||||
this.remaining = params.remaining;
|
const data = GuestChatQuotaSchema.parse(input);
|
||||||
this.date = params.date;
|
Object.assign(this, data);
|
||||||
Object.freeze(this);
|
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 {
|
needsReset(todayString: string): boolean {
|
||||||
return this.date !== todayString;
|
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)
|
* 原始 Dart: ImageUploadResponse (lib/data/models/chat/image_upload_response.dart)
|
||||||
*/
|
*/
|
||||||
export class ImageUploadResponse {
|
import { z } from "zod";
|
||||||
readonly success: boolean;
|
|
||||||
readonly imageId: string;
|
|
||||||
readonly thumbUrl: string;
|
|
||||||
readonly mediumUrl: string;
|
|
||||||
readonly originalUrl: string;
|
|
||||||
readonly width: number;
|
|
||||||
readonly height: number;
|
|
||||||
readonly bytes: number;
|
|
||||||
|
|
||||||
constructor(params: {
|
export const ImageUploadResponseSchema = z.object({
|
||||||
success?: boolean;
|
success: z.boolean().default(true),
|
||||||
imageId: string;
|
imageId: z.string(),
|
||||||
thumbUrl: string;
|
thumbUrl: z.string(),
|
||||||
mediumUrl: string;
|
mediumUrl: z.string(),
|
||||||
originalUrl: string;
|
originalUrl: z.string(),
|
||||||
width: number;
|
width: z.number(),
|
||||||
height: number;
|
height: z.number(),
|
||||||
bytes: number;
|
bytes: z.number(),
|
||||||
}) {
|
});
|
||||||
this.success = params.success ?? true;
|
|
||||||
this.imageId = params.imageId;
|
export type ImageUploadResponseInput = z.input<typeof ImageUploadResponseSchema>;
|
||||||
this.thumbUrl = params.thumbUrl;
|
export type ImageUploadResponseData = z.output<typeof ImageUploadResponseSchema>;
|
||||||
this.mediumUrl = params.mediumUrl;
|
|
||||||
this.originalUrl = params.originalUrl;
|
export class ImageUploadResponse {
|
||||||
this.width = params.width;
|
declare readonly success: boolean;
|
||||||
this.height = params.height;
|
declare readonly imageId: string;
|
||||||
this.bytes = params.bytes;
|
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);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: ImageUploadResponseInput): ImageUploadResponse {
|
||||||
return {
|
return new ImageUploadResponse(input);
|
||||||
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 fromJson(json: Record<string, unknown>): ImageUploadResponse {
|
static fromJson(json: unknown): ImageUploadResponse {
|
||||||
const requireString = (key: string): string => {
|
return ImageUploadResponse.from(json as ImageUploadResponseInput);
|
||||||
const v = json[key];
|
}
|
||||||
if (typeof v !== "string") {
|
|
||||||
throw new Error(
|
toJson(): ImageUploadResponseData {
|
||||||
`ImageUploadResponse.${key} is required and must be a string`
|
return ImageUploadResponseSchema.parse(this);
|
||||||
);
|
|
||||||
}
|
|
||||||
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"),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,82 +1,50 @@
|
|||||||
/**
|
/**
|
||||||
* 发送消息请求 - 用于 /api/chat/send 接口
|
* 发送消息请求 - 用于 /api/chat/send 接口
|
||||||
* 原始 Dart: SendMessageRequest (lib/data/models/chat/send_message_request.dart)
|
* 原始 Dart: SendMessageRequest (lib/data/models/chat/send_message_request.dart)
|
||||||
*
|
|
||||||
* 注:原始 Dart 中所有图片相关字段为可空,按"全部非空"约定兜底:
|
|
||||||
* - 字符串 → ""
|
|
||||||
* - 数字 → 0
|
|
||||||
*/
|
*/
|
||||||
export class SendMessageRequest {
|
import { z } from "zod";
|
||||||
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;
|
|
||||||
|
|
||||||
constructor(params: {
|
export const SendMessageRequestSchema = z.object({
|
||||||
message?: string;
|
message: z.string().default(""),
|
||||||
image?: string;
|
image: z.string().default(""),
|
||||||
imageId?: string;
|
imageId: z.string().default(""),
|
||||||
imageThumbUrl?: string;
|
imageThumbUrl: z.string().default(""),
|
||||||
imageMediumUrl?: string;
|
imageMediumUrl: z.string().default(""),
|
||||||
imageOriginalUrl?: string;
|
imageOriginalUrl: z.string().default(""),
|
||||||
imageWidth?: number;
|
imageWidth: z.number().default(0),
|
||||||
imageHeight?: number;
|
imageHeight: z.number().default(0),
|
||||||
useWebSocket?: boolean;
|
useWebSocket: z.boolean().default(false),
|
||||||
}) {
|
});
|
||||||
this.message = params.message ?? "";
|
|
||||||
this.image = params.image ?? "";
|
export type SendMessageRequestInput = z.input<typeof SendMessageRequestSchema>;
|
||||||
this.imageId = params.imageId ?? "";
|
export type SendMessageRequestData = z.output<typeof SendMessageRequestSchema>;
|
||||||
this.imageThumbUrl = params.imageThumbUrl ?? "";
|
|
||||||
this.imageMediumUrl = params.imageMediumUrl ?? "";
|
export class SendMessageRequest {
|
||||||
this.imageOriginalUrl = params.imageOriginalUrl ?? "";
|
declare readonly message: string;
|
||||||
this.imageWidth = params.imageWidth ?? 0;
|
declare readonly image: string;
|
||||||
this.imageHeight = params.imageHeight ?? 0;
|
declare readonly imageId: string;
|
||||||
this.useWebSocket = params.useWebSocket ?? false;
|
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);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: SendMessageRequestInput): SendMessageRequest {
|
||||||
return {
|
return new SendMessageRequest(input);
|
||||||
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 fromJson(json: Record<string, unknown>): SendMessageRequest {
|
static fromJson(json: unknown): SendMessageRequest {
|
||||||
return new SendMessageRequest({
|
return SendMessageRequest.from(json as SendMessageRequestInput);
|
||||||
message: typeof json.message === "string" ? json.message : undefined,
|
}
|
||||||
image: typeof json.image === "string" ? json.image : undefined,
|
|
||||||
imageId: typeof json.imageId === "string" ? json.imageId : undefined,
|
toJson(): SendMessageRequestData {
|
||||||
imageThumbUrl:
|
return SendMessageRequestSchema.parse(this);
|
||||||
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,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,23 +2,33 @@
|
|||||||
* STT(语音转文字)响应
|
* STT(语音转文字)响应
|
||||||
* 原始 Dart: SttData (lib/data/models/chat/stt_response.dart)
|
* 原始 Dart: SttData (lib/data/models/chat/stt_response.dart)
|
||||||
*/
|
*/
|
||||||
export class SttData {
|
import { z } from "zod";
|
||||||
readonly text: string;
|
|
||||||
|
|
||||||
constructor(params: { text: string }) {
|
export const SttDataSchema = z.object({
|
||||||
this.text = params.text;
|
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);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: SttDataInput): SttData {
|
||||||
return { text: this.text };
|
return new SttData(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): SttData {
|
static fromJson(json: unknown): SttData {
|
||||||
const text = json.text;
|
return SttData.from(json as SttDataInput);
|
||||||
if (typeof text !== "string") {
|
}
|
||||||
throw new Error("SttData.text is required and must be a string");
|
|
||||||
}
|
toJson(): SttDataData {
|
||||||
return new SttData({ text });
|
return SttDataSchema.parse(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,40 +2,37 @@
|
|||||||
* 待同步的单条消息
|
* 待同步的单条消息
|
||||||
* 原始 Dart: SyncMessage (lib/data/models/chat/chat_sync.dart)
|
* 原始 Dart: SyncMessage (lib/data/models/chat/chat_sync.dart)
|
||||||
*/
|
*/
|
||||||
export class SyncMessage {
|
import { z } from "zod";
|
||||||
readonly role: string;
|
|
||||||
readonly content: string;
|
|
||||||
readonly timestamp: string;
|
|
||||||
|
|
||||||
constructor(params: { role: string; content: string; timestamp: string }) {
|
export const SyncMessageSchema = z.object({
|
||||||
this.role = params.role;
|
role: z.string(),
|
||||||
this.content = params.content;
|
content: z.string(),
|
||||||
this.timestamp = params.timestamp;
|
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);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: SyncMessageInput): SyncMessage {
|
||||||
return {
|
return new SyncMessage(input);
|
||||||
role: this.role,
|
|
||||||
content: this.content,
|
|
||||||
timestamp: this.timestamp,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): SyncMessage {
|
static fromJson(json: unknown): SyncMessage {
|
||||||
const requireString = (key: string): string => {
|
return SyncMessage.from(json as SyncMessageInput);
|
||||||
const v = json[key];
|
}
|
||||||
if (typeof v !== "string") {
|
|
||||||
throw new Error(
|
toJson(): SyncMessageData {
|
||||||
`SyncMessage.${key} is required and must be a string`
|
return SyncMessageSchema.parse(this);
|
||||||
);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
};
|
|
||||||
return new SyncMessage({
|
|
||||||
role: requireString("role"),
|
|
||||||
content: requireString("content"),
|
|
||||||
timestamp: requireString("timestamp"),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,40 +2,37 @@
|
|||||||
* 应用事件上报请求 - 用于 /api/data/report-user-info 接口
|
* 应用事件上报请求 - 用于 /api/data/report-user-info 接口
|
||||||
* 原始 Dart: AppEvent (lib/data/models/metrics/app_event.dart)
|
* 原始 Dart: AppEvent (lib/data/models/metrics/app_event.dart)
|
||||||
*/
|
*/
|
||||||
export class AppEvent {
|
import { z } from "zod";
|
||||||
readonly userId: string;
|
|
||||||
readonly browser: string;
|
|
||||||
readonly userAgent: string;
|
|
||||||
|
|
||||||
constructor(params: { userId: string; browser: string; userAgent: string }) {
|
export const AppEventSchema = z.object({
|
||||||
this.userId = params.userId;
|
userId: z.string(),
|
||||||
this.browser = params.browser;
|
browser: z.string(),
|
||||||
this.userAgent = params.userAgent;
|
userAgent: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type AppEventInput = z.input<typeof AppEventSchema>;
|
||||||
|
export type AppEventData = z.output<typeof AppEventSchema>;
|
||||||
|
|
||||||
|
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);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: AppEventInput): AppEvent {
|
||||||
return {
|
return new AppEvent(input);
|
||||||
userId: this.userId,
|
|
||||||
browser: this.browser,
|
|
||||||
userAgent: this.userAgent,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): AppEvent {
|
static fromJson(json: unknown): AppEvent {
|
||||||
const requireString = (key: string): string => {
|
return AppEvent.from(json as AppEventInput);
|
||||||
const v = json[key];
|
}
|
||||||
if (typeof v !== "string") {
|
|
||||||
throw new Error(
|
toJson(): AppEventData {
|
||||||
`AppEvent.${key} is required and must be a string`
|
return AppEventSchema.parse(this);
|
||||||
);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
};
|
|
||||||
return new AppEvent({
|
|
||||||
userId: requireString("userId"),
|
|
||||||
browser: requireString("browser"),
|
|
||||||
userAgent: requireString("userAgent"),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,67 +2,41 @@
|
|||||||
* PWA 事件上报请求 - 用于 /api/metrics/pwa/event 接口
|
* PWA 事件上报请求 - 用于 /api/metrics/pwa/event 接口
|
||||||
* 原始 Dart: PwaEvent (lib/data/models/metrics/pwa_event.dart)
|
* 原始 Dart: PwaEvent (lib/data/models/metrics/pwa_event.dart)
|
||||||
*/
|
*/
|
||||||
export class PwaEvent {
|
import { z } from "zod";
|
||||||
readonly deviceId: string;
|
|
||||||
readonly deviceType: string;
|
|
||||||
readonly timestamp: number;
|
|
||||||
readonly pwaInstalled: boolean;
|
|
||||||
readonly pwaSupported: boolean;
|
|
||||||
|
|
||||||
constructor(params: {
|
export const PwaEventSchema = z.object({
|
||||||
deviceId: string;
|
deviceId: z.string(),
|
||||||
deviceType: string;
|
deviceType: z.string(),
|
||||||
timestamp: number;
|
timestamp: z.number(),
|
||||||
pwaInstalled?: boolean;
|
pwaInstalled: z.boolean().default(false),
|
||||||
pwaSupported?: boolean;
|
pwaSupported: z.boolean().default(false),
|
||||||
}) {
|
});
|
||||||
this.deviceId = params.deviceId;
|
|
||||||
this.deviceType = params.deviceType;
|
export type PwaEventInput = z.input<typeof PwaEventSchema>;
|
||||||
this.timestamp = params.timestamp;
|
export type PwaEventData = z.output<typeof PwaEventSchema>;
|
||||||
this.pwaInstalled = params.pwaInstalled ?? false;
|
|
||||||
this.pwaSupported = params.pwaSupported ?? false;
|
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);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: PwaEventInput): PwaEvent {
|
||||||
return {
|
return new PwaEvent(input);
|
||||||
deviceId: this.deviceId,
|
|
||||||
deviceType: this.deviceType,
|
|
||||||
timestamp: this.timestamp,
|
|
||||||
pwaInstalled: this.pwaInstalled,
|
|
||||||
pwaSupported: this.pwaSupported,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): PwaEvent {
|
static fromJson(json: unknown): PwaEvent {
|
||||||
const requireString = (key: string): string => {
|
return PwaEvent.from(json as PwaEventInput);
|
||||||
const v = json[key];
|
}
|
||||||
if (typeof v !== "string") {
|
|
||||||
throw new Error(
|
toJson(): PwaEventData {
|
||||||
`PwaEvent.${key} is required and must be a string`
|
return PwaEventSchema.parse(this);
|
||||||
);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
};
|
|
||||||
const requireNumber = (key: string): number => {
|
|
||||||
const v = json[key];
|
|
||||||
if (typeof v !== "number") {
|
|
||||||
throw new Error(`PwaEvent.${key} is required and must be a number`);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
};
|
|
||||||
return new PwaEvent({
|
|
||||||
deviceId: requireString("deviceId"),
|
|
||||||
deviceType: requireString("deviceType"),
|
|
||||||
timestamp: requireNumber("timestamp"),
|
|
||||||
pwaInstalled:
|
|
||||||
typeof json.pwaInstalled === "boolean"
|
|
||||||
? json.pwaInstalled
|
|
||||||
: undefined,
|
|
||||||
pwaSupported:
|
|
||||||
typeof json.pwaSupported === "boolean"
|
|
||||||
? json.pwaSupported
|
|
||||||
: undefined,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,35 @@
|
|||||||
/**
|
/**
|
||||||
* 头像数据模型 - 对应后端 AvatarData schema
|
* 头像数据模型 - 对应后端 AvatarData schema
|
||||||
* 原始 Dart: AvatarData (lib/data/models/user/user.dart)
|
* 原始 Dart: AvatarData (lib/data/models/user/user.dart)
|
||||||
*
|
|
||||||
* 注:原始 Dart 中 `avatarUrl` 为 `String?`,按"全部非空"约定兜底为空串。
|
|
||||||
*/
|
*/
|
||||||
export class AvatarData {
|
import { z } from "zod";
|
||||||
readonly avatarUrl: string;
|
|
||||||
readonly userId: string;
|
|
||||||
|
|
||||||
constructor(params: { avatarUrl?: string; userId: string }) {
|
export const AvatarDataSchema = z.object({
|
||||||
this.avatarUrl = params.avatarUrl ?? "";
|
avatarUrl: z.string().default(""),
|
||||||
this.userId = params.userId;
|
userId: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type AvatarDataInput = z.input<typeof AvatarDataSchema>;
|
||||||
|
export type AvatarDataData = z.output<typeof AvatarDataSchema>;
|
||||||
|
|
||||||
|
export class AvatarData {
|
||||||
|
declare readonly avatarUrl: string;
|
||||||
|
declare readonly userId: string;
|
||||||
|
|
||||||
|
private constructor(data: AvatarDataData) {
|
||||||
|
Object.assign(this, data);
|
||||||
Object.freeze(this);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: AvatarDataInput): AvatarData {
|
||||||
return {
|
return new AvatarData(AvatarDataSchema.parse(input));
|
||||||
avatarUrl: this.avatarUrl,
|
|
||||||
userId: this.userId,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): AvatarData {
|
static fromJson(json: unknown): AvatarData {
|
||||||
const userId = json.userId;
|
return AvatarData.from(json as AvatarDataInput);
|
||||||
if (typeof userId !== "string") {
|
}
|
||||||
throw new Error("AvatarData.userId is required and must be a string");
|
|
||||||
}
|
toJson(): AvatarDataData {
|
||||||
return new AvatarData({
|
return AvatarDataSchema.parse(this);
|
||||||
userId,
|
|
||||||
avatarUrl:
|
|
||||||
typeof json.avatarUrl === "string" ? json.avatarUrl : undefined,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,34 +2,34 @@
|
|||||||
* 积分数据模型 - 对应后端 CreditsData schema
|
* 积分数据模型 - 对应后端 CreditsData schema
|
||||||
* 原始 Dart: CreditsData (lib/data/models/user/user.dart)
|
* 原始 Dart: CreditsData (lib/data/models/user/user.dart)
|
||||||
*/
|
*/
|
||||||
export class CreditsData {
|
import { z } from "zod";
|
||||||
readonly userId: string;
|
|
||||||
readonly dolBalance: number;
|
|
||||||
|
|
||||||
constructor(params: { userId: string; dolBalance: number }) {
|
export const CreditsDataSchema = z.object({
|
||||||
this.userId = params.userId;
|
userId: z.string(),
|
||||||
this.dolBalance = params.dolBalance;
|
dolBalance: z.number(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type CreditsDataInput = z.input<typeof CreditsDataSchema>;
|
||||||
|
export type CreditsDataData = z.output<typeof CreditsDataSchema>;
|
||||||
|
|
||||||
|
export class CreditsData {
|
||||||
|
declare readonly userId: string;
|
||||||
|
declare readonly dolBalance: number;
|
||||||
|
|
||||||
|
private constructor(data: CreditsDataData) {
|
||||||
|
Object.assign(this, data);
|
||||||
Object.freeze(this);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: CreditsDataInput): CreditsData {
|
||||||
return {
|
return new CreditsData(CreditsDataSchema.parse(input));
|
||||||
userId: this.userId,
|
|
||||||
dolBalance: this.dolBalance,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): CreditsData {
|
static fromJson(json: unknown): CreditsData {
|
||||||
const userId = json.userId;
|
return CreditsData.from(json as CreditsDataInput);
|
||||||
const dolBalance = json.dolBalance;
|
}
|
||||||
if (typeof userId !== "string") {
|
|
||||||
throw new Error("CreditsData.userId is required and must be a string");
|
toJson(): CreditsDataData {
|
||||||
}
|
return CreditsDataSchema.parse(this);
|
||||||
if (typeof dolBalance !== "number") {
|
|
||||||
throw new Error(
|
|
||||||
"CreditsData.dolBalance is required and must be a number"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return new CreditsData({ userId, dolBalance });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +1,39 @@
|
|||||||
/**
|
/**
|
||||||
* 积分历史记录模型 - 对应后端 CreditsHistoryData schema
|
* 积分历史记录模型 - 对应后端 CreditsHistoryData schema
|
||||||
* 原始 Dart: CreditsHistoryData (lib/data/models/user/user.dart)
|
* 原始 Dart: CreditsHistoryData (lib/data/models/user/user.dart)
|
||||||
*
|
|
||||||
* 注:原始 Dart 中 `records` 为 `List<Map<String, dynamic>>`,
|
|
||||||
* 迁移为 `Record<string, unknown>[]`。
|
|
||||||
*/
|
*/
|
||||||
export class CreditsHistoryData {
|
import { z } from "zod";
|
||||||
readonly records: Record<string, unknown>[];
|
|
||||||
readonly total: number;
|
|
||||||
readonly limit: number;
|
|
||||||
readonly offset: number;
|
|
||||||
|
|
||||||
constructor(params: {
|
export const CreditsHistoryDataSchema = z.object({
|
||||||
records: Record<string, unknown>[];
|
records: z.array(z.record(z.string(), z.unknown())),
|
||||||
total: number;
|
total: z.number(),
|
||||||
limit: number;
|
limit: z.number(),
|
||||||
offset: number;
|
offset: z.number(),
|
||||||
}) {
|
});
|
||||||
this.records = params.records;
|
|
||||||
this.total = params.total;
|
export type CreditsHistoryDataInput = z.input<typeof CreditsHistoryDataSchema>;
|
||||||
this.limit = params.limit;
|
export type CreditsHistoryDataData = z.output<typeof CreditsHistoryDataSchema>;
|
||||||
this.offset = params.offset;
|
|
||||||
|
export class CreditsHistoryData {
|
||||||
|
declare readonly records: Record<string, unknown>[];
|
||||||
|
declare readonly total: number;
|
||||||
|
declare readonly limit: number;
|
||||||
|
declare readonly offset: number;
|
||||||
|
|
||||||
|
private constructor(data: CreditsHistoryDataData) {
|
||||||
|
Object.assign(this, data);
|
||||||
Object.freeze(this);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: CreditsHistoryDataInput): CreditsHistoryData {
|
||||||
return {
|
return new CreditsHistoryData(CreditsHistoryDataSchema.parse(input));
|
||||||
records: this.records,
|
|
||||||
total: this.total,
|
|
||||||
limit: this.limit,
|
|
||||||
offset: this.offset,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): CreditsHistoryData {
|
static fromJson(json: unknown): CreditsHistoryData {
|
||||||
const requireNumber = (key: string): number => {
|
return CreditsHistoryData.from(json as CreditsHistoryDataInput);
|
||||||
const v = json[key];
|
}
|
||||||
if (typeof v !== "number") {
|
|
||||||
throw new Error(
|
toJson(): CreditsHistoryDataData {
|
||||||
`CreditsHistoryData.${key} is required and must be a number`
|
return CreditsHistoryDataSchema.parse(this);
|
||||||
);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
};
|
|
||||||
const rawRecords = json.records;
|
|
||||||
const records: Record<string, unknown>[] = Array.isArray(rawRecords)
|
|
||||||
? (rawRecords as Record<string, unknown>[])
|
|
||||||
: [];
|
|
||||||
return new CreditsHistoryData({
|
|
||||||
records,
|
|
||||||
total: requireNumber("total"),
|
|
||||||
limit: requireNumber("limit"),
|
|
||||||
offset: requireNumber("offset"),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,48 +1,49 @@
|
|||||||
/**
|
/**
|
||||||
* 个性特征模型
|
* 个性特征模型
|
||||||
* 原始 Dart: PersonalityTraits (lib/data/models/user/user.dart)
|
* 原始 Dart: PersonalityTraits (lib/data/models/user/user.dart)
|
||||||
*
|
|
||||||
* 注:所有 double 字段在 Dart 中使用 @Default(0.5)。
|
|
||||||
*/
|
*/
|
||||||
export class PersonalityTraits {
|
import { z } from "zod";
|
||||||
readonly cheerful: number;
|
|
||||||
readonly caring: number;
|
|
||||||
readonly playful: number;
|
|
||||||
readonly serious: number;
|
|
||||||
readonly romantic: number;
|
|
||||||
|
|
||||||
constructor(params: {
|
export const PERSONALITY_TRAITS_DEFAULTS: PersonalityTraitsData = {
|
||||||
cheerful?: number;
|
cheerful: 0.5,
|
||||||
caring?: number;
|
caring: 0.5,
|
||||||
playful?: number;
|
playful: 0.5,
|
||||||
serious?: number;
|
serious: 0.5,
|
||||||
romantic?: number;
|
romantic: 0.5,
|
||||||
}) {
|
};
|
||||||
this.cheerful = params.cheerful ?? 0.5;
|
|
||||||
this.caring = params.caring ?? 0.5;
|
export const PersonalityTraitsSchema = z.object({
|
||||||
this.playful = params.playful ?? 0.5;
|
cheerful: z.number().default(0.5),
|
||||||
this.serious = params.serious ?? 0.5;
|
caring: z.number().default(0.5),
|
||||||
this.romantic = params.romantic ?? 0.5;
|
playful: z.number().default(0.5),
|
||||||
|
serious: z.number().default(0.5),
|
||||||
|
romantic: z.number().default(0.5),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type PersonalityTraitsInput = z.input<typeof PersonalityTraitsSchema>;
|
||||||
|
export type PersonalityTraitsData = z.output<typeof PersonalityTraitsSchema>;
|
||||||
|
|
||||||
|
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(data: PersonalityTraitsData) {
|
||||||
|
Object.assign(this, data);
|
||||||
Object.freeze(this);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: PersonalityTraitsInput): PersonalityTraits {
|
||||||
return {
|
return new PersonalityTraits(PersonalityTraitsSchema.parse(input));
|
||||||
cheerful: this.cheerful,
|
|
||||||
caring: this.caring,
|
|
||||||
playful: this.playful,
|
|
||||||
serious: this.serious,
|
|
||||||
romantic: this.romantic,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): PersonalityTraits {
|
static fromJson(json: unknown): PersonalityTraits {
|
||||||
return new PersonalityTraits({
|
return PersonalityTraits.from(json as PersonalityTraitsInput);
|
||||||
cheerful: typeof json.cheerful === "number" ? json.cheerful : undefined,
|
}
|
||||||
caring: typeof json.caring === "number" ? json.caring : undefined,
|
|
||||||
playful: typeof json.playful === "number" ? json.playful : undefined,
|
toJson(): PersonalityTraitsData {
|
||||||
serious: typeof json.serious === "number" ? json.serious : undefined,
|
return PersonalityTraitsSchema.parse(this);
|
||||||
romantic: typeof json.romantic === "number" ? json.romantic : undefined,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,59 +1,39 @@
|
|||||||
/**
|
/**
|
||||||
* 最近记忆模型
|
* 最近记忆模型
|
||||||
* 原始 Dart: RecentMemory (lib/data/models/user/recent_memory.dart)
|
* 原始 Dart: RecentMemory (lib/data/models/user/recent_memory.dart)
|
||||||
*
|
|
||||||
* 注:原始 Dart 中 `createdAt` 为 `String?`,按"全部非空"约定兜底为空串。
|
|
||||||
*/
|
*/
|
||||||
export class RecentMemory {
|
import { z } from "zod";
|
||||||
readonly type: string;
|
|
||||||
readonly content: string;
|
|
||||||
readonly importance: number;
|
|
||||||
readonly createdAt: string;
|
|
||||||
|
|
||||||
constructor(params: {
|
export const RecentMemorySchema = z.object({
|
||||||
type: string;
|
type: z.string(),
|
||||||
content: string;
|
content: z.string(),
|
||||||
importance: number;
|
importance: z.number(),
|
||||||
createdAt?: string;
|
createdAt: z.string().default(""),
|
||||||
}) {
|
});
|
||||||
this.type = params.type;
|
|
||||||
this.content = params.content;
|
export type RecentMemoryInput = z.input<typeof RecentMemorySchema>;
|
||||||
this.importance = params.importance;
|
export type RecentMemoryData = z.output<typeof RecentMemorySchema>;
|
||||||
this.createdAt = params.createdAt ?? "";
|
|
||||||
|
export class RecentMemory {
|
||||||
|
declare readonly type: string;
|
||||||
|
declare readonly content: string;
|
||||||
|
declare readonly importance: number;
|
||||||
|
declare readonly createdAt: string;
|
||||||
|
|
||||||
|
private constructor(data: RecentMemoryData) {
|
||||||
|
Object.assign(this, data);
|
||||||
Object.freeze(this);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: RecentMemoryInput): RecentMemory {
|
||||||
return {
|
return new RecentMemory(RecentMemorySchema.parse(input));
|
||||||
type: this.type,
|
|
||||||
content: this.content,
|
|
||||||
importance: this.importance,
|
|
||||||
createdAt: this.createdAt,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): RecentMemory {
|
static fromJson(json: unknown): RecentMemory {
|
||||||
const requireString = (key: string): string => {
|
return RecentMemory.from(json as RecentMemoryInput);
|
||||||
const v = json[key];
|
}
|
||||||
if (typeof v !== "string") {
|
|
||||||
throw new Error(
|
toJson(): RecentMemoryData {
|
||||||
`RecentMemory.${key} is required and must be a string`
|
return RecentMemorySchema.parse(this);
|
||||||
);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
};
|
|
||||||
const importance = json.importance;
|
|
||||||
if (typeof importance !== "number") {
|
|
||||||
throw new Error(
|
|
||||||
"RecentMemory.importance is required and must be a number"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return new RecentMemory({
|
|
||||||
type: requireString("type"),
|
|
||||||
content: requireString("content"),
|
|
||||||
importance,
|
|
||||||
createdAt:
|
|
||||||
typeof json.createdAt === "string" ? json.createdAt : undefined,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +1,39 @@
|
|||||||
/**
|
/**
|
||||||
* 更新个人资料请求
|
* 更新个人资料请求
|
||||||
* 原始 Dart: UpdateProfileRequest (lib/data/models/user/user.dart)
|
* 原始 Dart: UpdateProfileRequest (lib/data/models/user/user.dart)
|
||||||
*
|
|
||||||
* 注:原始 Dart 中所有字段为 `String?`,按"全部非空"约定兜底为空串。
|
|
||||||
*/
|
*/
|
||||||
export class UpdateProfileRequest {
|
import { z } from "zod";
|
||||||
readonly username: string;
|
|
||||||
readonly nickname: string;
|
|
||||||
readonly preferredLanguage: string;
|
|
||||||
readonly currentMood: string;
|
|
||||||
|
|
||||||
constructor(params: {
|
export const UpdateProfileRequestSchema = z.object({
|
||||||
username?: string;
|
username: z.string().default(""),
|
||||||
nickname?: string;
|
nickname: z.string().default(""),
|
||||||
preferredLanguage?: string;
|
preferredLanguage: z.string().default(""),
|
||||||
currentMood?: string;
|
currentMood: z.string().default(""),
|
||||||
}) {
|
});
|
||||||
this.username = params.username ?? "";
|
|
||||||
this.nickname = params.nickname ?? "";
|
export type UpdateProfileRequestInput = z.input<typeof UpdateProfileRequestSchema>;
|
||||||
this.preferredLanguage = params.preferredLanguage ?? "";
|
export type UpdateProfileRequestData = z.output<typeof UpdateProfileRequestSchema>;
|
||||||
this.currentMood = params.currentMood ?? "";
|
|
||||||
|
export class UpdateProfileRequest {
|
||||||
|
declare readonly username: string;
|
||||||
|
declare readonly nickname: string;
|
||||||
|
declare readonly preferredLanguage: string;
|
||||||
|
declare readonly currentMood: string;
|
||||||
|
|
||||||
|
private constructor(data: UpdateProfileRequestData) {
|
||||||
|
Object.assign(this, data);
|
||||||
Object.freeze(this);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: UpdateProfileRequestInput): UpdateProfileRequest {
|
||||||
return {
|
return new UpdateProfileRequest(UpdateProfileRequestSchema.parse(input));
|
||||||
username: this.username,
|
|
||||||
nickname: this.nickname,
|
|
||||||
preferredLanguage: this.preferredLanguage,
|
|
||||||
currentMood: this.currentMood,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): UpdateProfileRequest {
|
static fromJson(json: unknown): UpdateProfileRequest {
|
||||||
return new UpdateProfileRequest({
|
return UpdateProfileRequest.from(json as UpdateProfileRequestInput);
|
||||||
username: typeof json.username === "string" ? json.username : undefined,
|
}
|
||||||
nickname: typeof json.nickname === "string" ? json.nickname : undefined,
|
|
||||||
preferredLanguage:
|
toJson(): UpdateProfileRequestData {
|
||||||
typeof json.preferredLanguage === "string"
|
return UpdateProfileRequestSchema.parse(this);
|
||||||
? json.preferredLanguage
|
|
||||||
: undefined,
|
|
||||||
currentMood:
|
|
||||||
typeof json.currentMood === "string" ? json.currentMood : undefined,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+63
-114
@@ -1,128 +1,77 @@
|
|||||||
/**
|
/**
|
||||||
* 用户模型 - 对应后端 UserData schema
|
* 用户模型 - 对应后端 UserData schema
|
||||||
* 原始 Dart: User (lib/data/models/user/user.dart)
|
* 原始 Dart: User (lib/data/models/user/user.dart)
|
||||||
*
|
|
||||||
* 注:原始 Dart 中 `email`/`personalityTraits`/`preferredLanguage`/`createdAt`/
|
|
||||||
* `lastMessageAt`/`avatarUrl` 为可空字段,按"全部非空"约定统一兜底:
|
|
||||||
* - 字符串字段 → 空串 ""
|
|
||||||
* - PersonalityTraits → 默认实例(全 0.5)
|
|
||||||
*/
|
*/
|
||||||
import { PersonalityTraits } from "./personality_traits";
|
import { z } from "zod";
|
||||||
|
import {
|
||||||
|
PersonalityTraits,
|
||||||
|
PersonalityTraitsSchema,
|
||||||
|
PERSONALITY_TRAITS_DEFAULTS,
|
||||||
|
} from "./personality_traits";
|
||||||
|
|
||||||
|
export const UserSchema = z.object({
|
||||||
|
id: z.string(),
|
||||||
|
username: z.string(),
|
||||||
|
email: z.string().default(""),
|
||||||
|
platform: z.string().default("web"),
|
||||||
|
intimacy: z.number().default(0),
|
||||||
|
dolBalance: z.number().default(0),
|
||||||
|
relationshipStage: z.string().default("密友"),
|
||||||
|
currentMood: z.string().default("happy"),
|
||||||
|
personalityTraits: PersonalityTraitsSchema.default(
|
||||||
|
PERSONALITY_TRAITS_DEFAULTS
|
||||||
|
),
|
||||||
|
preferredLanguage: z.string().default(""),
|
||||||
|
createdAt: z.string().default(""),
|
||||||
|
lastMessageAt: z.string().default(""),
|
||||||
|
avatarUrl: z.string().default(""),
|
||||||
|
loginProvider: z.string().default("email"),
|
||||||
|
isGuest: z.boolean().default(false),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type UserInput = z.input<typeof UserSchema>;
|
||||||
|
export type UserData = z.output<typeof UserSchema>;
|
||||||
|
|
||||||
export class User {
|
export class User {
|
||||||
readonly id: string;
|
declare readonly id: string;
|
||||||
readonly username: string;
|
declare readonly username: string;
|
||||||
readonly email: string;
|
declare readonly email: string;
|
||||||
readonly platform: string;
|
declare readonly platform: string;
|
||||||
readonly intimacy: number;
|
declare readonly intimacy: number;
|
||||||
readonly dolBalance: number;
|
declare readonly dolBalance: number;
|
||||||
readonly relationshipStage: string;
|
declare readonly relationshipStage: string;
|
||||||
readonly currentMood: string;
|
declare readonly currentMood: string;
|
||||||
readonly personalityTraits: PersonalityTraits;
|
declare readonly personalityTraits: PersonalityTraits;
|
||||||
readonly preferredLanguage: string;
|
declare readonly preferredLanguage: string;
|
||||||
readonly createdAt: string;
|
declare readonly createdAt: string;
|
||||||
readonly lastMessageAt: string;
|
declare readonly lastMessageAt: string;
|
||||||
readonly avatarUrl: string;
|
declare readonly avatarUrl: string;
|
||||||
readonly loginProvider: string;
|
declare readonly loginProvider: string;
|
||||||
readonly isGuest: boolean;
|
declare readonly isGuest: boolean;
|
||||||
|
|
||||||
constructor(params: {
|
private constructor(input: UserInput) {
|
||||||
id: string;
|
const data = UserSchema.parse(input);
|
||||||
username: string;
|
Object.assign(this, data);
|
||||||
email?: string;
|
|
||||||
platform?: string;
|
|
||||||
intimacy?: number;
|
|
||||||
dolBalance?: number;
|
|
||||||
relationshipStage?: string;
|
|
||||||
currentMood?: string;
|
|
||||||
personalityTraits?: PersonalityTraits;
|
|
||||||
preferredLanguage?: string;
|
|
||||||
createdAt?: string;
|
|
||||||
lastMessageAt?: string;
|
|
||||||
avatarUrl?: string;
|
|
||||||
loginProvider?: string;
|
|
||||||
isGuest?: boolean;
|
|
||||||
}) {
|
|
||||||
this.id = params.id;
|
|
||||||
this.username = params.username;
|
|
||||||
this.email = params.email ?? "";
|
|
||||||
this.platform = params.platform ?? "web";
|
|
||||||
this.intimacy = params.intimacy ?? 0;
|
|
||||||
this.dolBalance = params.dolBalance ?? 0;
|
|
||||||
this.relationshipStage = params.relationshipStage ?? "密友";
|
|
||||||
this.currentMood = params.currentMood ?? "happy";
|
|
||||||
this.personalityTraits = params.personalityTraits ?? new PersonalityTraits({});
|
|
||||||
this.preferredLanguage = params.preferredLanguage ?? "";
|
|
||||||
this.createdAt = params.createdAt ?? "";
|
|
||||||
this.lastMessageAt = params.lastMessageAt ?? "";
|
|
||||||
this.avatarUrl = params.avatarUrl ?? "";
|
|
||||||
this.loginProvider = params.loginProvider ?? "email";
|
|
||||||
this.isGuest = params.isGuest ?? false;
|
|
||||||
Object.freeze(this);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: UserInput): User {
|
||||||
return {
|
return new User(input);
|
||||||
id: this.id,
|
|
||||||
username: this.username,
|
|
||||||
email: this.email,
|
|
||||||
platform: this.platform,
|
|
||||||
intimacy: this.intimacy,
|
|
||||||
dolBalance: this.dolBalance,
|
|
||||||
relationshipStage: this.relationshipStage,
|
|
||||||
currentMood: this.currentMood,
|
|
||||||
personalityTraits: this.personalityTraits.toJson(),
|
|
||||||
preferredLanguage: this.preferredLanguage,
|
|
||||||
createdAt: this.createdAt,
|
|
||||||
lastMessageAt: this.lastMessageAt,
|
|
||||||
avatarUrl: this.avatarUrl,
|
|
||||||
loginProvider: this.loginProvider,
|
|
||||||
isGuest: this.isGuest,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): User {
|
static fromJson(json: unknown): User {
|
||||||
const requireString = (key: string): string => {
|
return User.from(json as UserInput);
|
||||||
const v = json[key];
|
}
|
||||||
if (typeof v !== "string") {
|
|
||||||
throw new Error(`User.${key} is required and must be a string`);
|
/**
|
||||||
}
|
* 创建一个具有空 id/username 的 User 实例,用于可选 user 字段的默认占位。
|
||||||
return v;
|
* 调用方可通过 `user.id === ""` 判定"无用户数据"。
|
||||||
};
|
*/
|
||||||
return new User({
|
static empty(): User {
|
||||||
id: requireString("id"),
|
return new User({ id: "", username: "" });
|
||||||
username: requireString("username"),
|
}
|
||||||
email: typeof json.email === "string" ? json.email : undefined,
|
|
||||||
platform: typeof json.platform === "string" ? json.platform : undefined,
|
toJson(): UserData {
|
||||||
intimacy: typeof json.intimacy === "number" ? json.intimacy : undefined,
|
return UserSchema.parse(this);
|
||||||
dolBalance:
|
|
||||||
typeof json.dolBalance === "number" ? json.dolBalance : undefined,
|
|
||||||
relationshipStage:
|
|
||||||
typeof json.relationshipStage === "string"
|
|
||||||
? json.relationshipStage
|
|
||||||
: undefined,
|
|
||||||
currentMood:
|
|
||||||
typeof json.currentMood === "string" ? json.currentMood : undefined,
|
|
||||||
personalityTraits: json.personalityTraits
|
|
||||||
? PersonalityTraits.fromJson(
|
|
||||||
json.personalityTraits as Record<string, unknown>
|
|
||||||
)
|
|
||||||
: undefined,
|
|
||||||
preferredLanguage:
|
|
||||||
typeof json.preferredLanguage === "string"
|
|
||||||
? json.preferredLanguage
|
|
||||||
: undefined,
|
|
||||||
createdAt:
|
|
||||||
typeof json.createdAt === "string" ? json.createdAt : undefined,
|
|
||||||
lastMessageAt:
|
|
||||||
typeof json.lastMessageAt === "string" ? json.lastMessageAt : undefined,
|
|
||||||
avatarUrl:
|
|
||||||
typeof json.avatarUrl === "string" ? json.avatarUrl : undefined,
|
|
||||||
loginProvider:
|
|
||||||
typeof json.loginProvider === "string"
|
|
||||||
? json.loginProvider
|
|
||||||
: undefined,
|
|
||||||
isGuest: typeof json.isGuest === "boolean" ? json.isGuest : undefined,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,156 +1,71 @@
|
|||||||
/**
|
/**
|
||||||
* 用户统计响应
|
* 用户统计响应
|
||||||
* 原始 Dart: UserStatsResponse (lib/data/models/user/user_stats_response.dart)
|
* 原始 Dart: UserStatsResponse (lib/data/models/user/user_stats_response.dart)
|
||||||
*
|
|
||||||
* 注:原始 Dart 中 `lastMessageAt`/`accountCreatedAt`/`currentMood` 为 `String?`,
|
|
||||||
* `personalityTraits` 为 `PersonalityTraits?`,按"全部非空"约定兜底:
|
|
||||||
* - 字符串 → ""
|
|
||||||
* - PersonalityTraits → 默认实例
|
|
||||||
* - recentMemories 缺失或 null 时使用 []
|
|
||||||
*/
|
*/
|
||||||
import { PersonalityTraits } from "./personality_traits";
|
import { z } from "zod";
|
||||||
import { RecentMemory } from "./recent_memory";
|
import {
|
||||||
|
PersonalityTraits,
|
||||||
|
PERSONALITY_TRAITS_DEFAULTS,
|
||||||
|
PersonalityTraitsSchema,
|
||||||
|
} from "./personality_traits";
|
||||||
|
import { RecentMemory, RecentMemorySchema } from "./recent_memory";
|
||||||
|
|
||||||
|
export const UserStatsResponseSchema = z.object({
|
||||||
|
userId: z.string(),
|
||||||
|
username: z.string(),
|
||||||
|
platform: z.string(),
|
||||||
|
intimacy: z.number(),
|
||||||
|
relationshipStage: z.string(),
|
||||||
|
dolBalance: z.number(),
|
||||||
|
totalMessages: z.number(),
|
||||||
|
lastMessageAt: z.string().default(""),
|
||||||
|
accountCreatedAt: z.string().default(""),
|
||||||
|
currentMood: z.string().default(""),
|
||||||
|
personalityTraits: PersonalityTraitsSchema.default(
|
||||||
|
PERSONALITY_TRAITS_DEFAULTS
|
||||||
|
),
|
||||||
|
recentMemories: z.array(RecentMemorySchema).default([]),
|
||||||
|
promptTokensTotal: z.number().default(0),
|
||||||
|
completionTokensTotal: z.number().default(0),
|
||||||
|
cacheHitTokensTotal: z.number().default(0),
|
||||||
|
tokensTotal: z.number().default(0),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type UserStatsResponseInput = z.input<typeof UserStatsResponseSchema>;
|
||||||
|
export type UserStatsResponseData = z.output<typeof UserStatsResponseSchema>;
|
||||||
|
|
||||||
export class UserStatsResponse {
|
export class UserStatsResponse {
|
||||||
readonly userId: string;
|
declare readonly userId: string;
|
||||||
readonly username: string;
|
declare readonly username: string;
|
||||||
readonly platform: string;
|
declare readonly platform: string;
|
||||||
readonly intimacy: number;
|
declare readonly intimacy: number;
|
||||||
readonly relationshipStage: string;
|
declare readonly relationshipStage: string;
|
||||||
readonly dolBalance: number;
|
declare readonly dolBalance: number;
|
||||||
readonly totalMessages: number;
|
declare readonly totalMessages: number;
|
||||||
readonly lastMessageAt: string;
|
declare readonly lastMessageAt: string;
|
||||||
readonly accountCreatedAt: string;
|
declare readonly accountCreatedAt: string;
|
||||||
readonly currentMood: string;
|
declare readonly currentMood: string;
|
||||||
readonly personalityTraits: PersonalityTraits;
|
declare readonly personalityTraits: PersonalityTraits;
|
||||||
readonly recentMemories: RecentMemory[];
|
declare readonly recentMemories: RecentMemory[];
|
||||||
readonly promptTokensTotal: number;
|
declare readonly promptTokensTotal: number;
|
||||||
readonly completionTokensTotal: number;
|
declare readonly completionTokensTotal: number;
|
||||||
readonly cacheHitTokensTotal: number;
|
declare readonly cacheHitTokensTotal: number;
|
||||||
readonly tokensTotal: number;
|
declare readonly tokensTotal: number;
|
||||||
|
|
||||||
constructor(params: {
|
private constructor(data: UserStatsResponseData) {
|
||||||
userId: string;
|
Object.assign(this, data);
|
||||||
username: string;
|
|
||||||
platform: string;
|
|
||||||
intimacy: number;
|
|
||||||
relationshipStage: string;
|
|
||||||
dolBalance: number;
|
|
||||||
totalMessages: number;
|
|
||||||
lastMessageAt?: string;
|
|
||||||
accountCreatedAt?: string;
|
|
||||||
currentMood?: string;
|
|
||||||
personalityTraits?: PersonalityTraits;
|
|
||||||
recentMemories?: RecentMemory[];
|
|
||||||
promptTokensTotal?: number;
|
|
||||||
completionTokensTotal?: number;
|
|
||||||
cacheHitTokensTotal?: number;
|
|
||||||
tokensTotal?: number;
|
|
||||||
}) {
|
|
||||||
this.userId = params.userId;
|
|
||||||
this.username = params.username;
|
|
||||||
this.platform = params.platform;
|
|
||||||
this.intimacy = params.intimacy;
|
|
||||||
this.relationshipStage = params.relationshipStage;
|
|
||||||
this.dolBalance = params.dolBalance;
|
|
||||||
this.totalMessages = params.totalMessages;
|
|
||||||
this.lastMessageAt = params.lastMessageAt ?? "";
|
|
||||||
this.accountCreatedAt = params.accountCreatedAt ?? "";
|
|
||||||
this.currentMood = params.currentMood ?? "";
|
|
||||||
this.personalityTraits =
|
|
||||||
params.personalityTraits ?? new PersonalityTraits({});
|
|
||||||
this.recentMemories = params.recentMemories ?? [];
|
|
||||||
this.promptTokensTotal = params.promptTokensTotal ?? 0;
|
|
||||||
this.completionTokensTotal = params.completionTokensTotal ?? 0;
|
|
||||||
this.cacheHitTokensTotal = params.cacheHitTokensTotal ?? 0;
|
|
||||||
this.tokensTotal = params.tokensTotal ?? 0;
|
|
||||||
Object.freeze(this);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): Record<string, unknown> {
|
static from(input: UserStatsResponseInput): UserStatsResponse {
|
||||||
return {
|
return new UserStatsResponse(UserStatsResponseSchema.parse(input));
|
||||||
userId: this.userId,
|
|
||||||
username: this.username,
|
|
||||||
platform: this.platform,
|
|
||||||
intimacy: this.intimacy,
|
|
||||||
relationshipStage: this.relationshipStage,
|
|
||||||
dolBalance: this.dolBalance,
|
|
||||||
totalMessages: this.totalMessages,
|
|
||||||
lastMessageAt: this.lastMessageAt,
|
|
||||||
accountCreatedAt: this.accountCreatedAt,
|
|
||||||
currentMood: this.currentMood,
|
|
||||||
personalityTraits: this.personalityTraits.toJson(),
|
|
||||||
recentMemories: this.recentMemories.map((m) => m.toJson()),
|
|
||||||
promptTokensTotal: this.promptTokensTotal,
|
|
||||||
completionTokensTotal: this.completionTokensTotal,
|
|
||||||
cacheHitTokensTotal: this.cacheHitTokensTotal,
|
|
||||||
tokensTotal: this.tokensTotal,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json: Record<string, unknown>): UserStatsResponse {
|
static fromJson(json: unknown): UserStatsResponse {
|
||||||
const requireString = (key: string): string => {
|
return UserStatsResponse.from(json as UserStatsResponseInput);
|
||||||
const v = json[key];
|
}
|
||||||
if (typeof v !== "string") {
|
|
||||||
throw new Error(
|
toJson(): UserStatsResponseData {
|
||||||
`UserStatsResponse.${key} is required and must be a string`
|
return UserStatsResponseSchema.parse(this);
|
||||||
);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
};
|
|
||||||
const requireNumber = (key: string): number => {
|
|
||||||
const v = json[key];
|
|
||||||
if (typeof v !== "number") {
|
|
||||||
throw new Error(
|
|
||||||
`UserStatsResponse.${key} is required and must be a number`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
};
|
|
||||||
const rawMemories = json.recentMemories;
|
|
||||||
const recentMemories: RecentMemory[] = Array.isArray(rawMemories)
|
|
||||||
? (rawMemories as Record<string, unknown>[]).map((m) =>
|
|
||||||
RecentMemory.fromJson(m)
|
|
||||||
)
|
|
||||||
: [];
|
|
||||||
return new UserStatsResponse({
|
|
||||||
userId: requireString("userId"),
|
|
||||||
username: requireString("username"),
|
|
||||||
platform: requireString("platform"),
|
|
||||||
intimacy: requireNumber("intimacy"),
|
|
||||||
relationshipStage: requireString("relationshipStage"),
|
|
||||||
dolBalance: requireNumber("dolBalance"),
|
|
||||||
totalMessages: requireNumber("totalMessages"),
|
|
||||||
lastMessageAt:
|
|
||||||
typeof json.lastMessageAt === "string"
|
|
||||||
? json.lastMessageAt
|
|
||||||
: undefined,
|
|
||||||
accountCreatedAt:
|
|
||||||
typeof json.accountCreatedAt === "string"
|
|
||||||
? json.accountCreatedAt
|
|
||||||
: undefined,
|
|
||||||
currentMood:
|
|
||||||
typeof json.currentMood === "string" ? json.currentMood : undefined,
|
|
||||||
personalityTraits: json.personalityTraits
|
|
||||||
? PersonalityTraits.fromJson(
|
|
||||||
json.personalityTraits as Record<string, unknown>
|
|
||||||
)
|
|
||||||
: undefined,
|
|
||||||
recentMemories,
|
|
||||||
promptTokensTotal:
|
|
||||||
typeof json.promptTokensTotal === "number"
|
|
||||||
? json.promptTokensTotal
|
|
||||||
: undefined,
|
|
||||||
completionTokensTotal:
|
|
||||||
typeof json.completionTokensTotal === "number"
|
|
||||||
? json.completionTokensTotal
|
|
||||||
: undefined,
|
|
||||||
cacheHitTokensTotal:
|
|
||||||
typeof json.cacheHitTokensTotal === "number"
|
|
||||||
? json.cacheHitTokensTotal
|
|
||||||
: undefined,
|
|
||||||
tokensTotal:
|
|
||||||
typeof json.tokensTotal === "number" ? json.tokensTotal : undefined,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user