refactor(data): merge DTO models into schemas
This commit is contained in:
@@ -12,7 +12,7 @@ description: Sync this Next.js frontend with backend API documentation
|
|||||||
当后端 API 新增、删除、字段结构调整、响应语义变化时,使用本 skill 更新:
|
当后端 API 新增、删除、字段结构调整、响应语义变化时,使用本 skill 更新:
|
||||||
|
|
||||||
- 网络层
|
- 网络层
|
||||||
- schema / DTO
|
- schema / 不可变数据类
|
||||||
- repository
|
- repository
|
||||||
- storage
|
- storage
|
||||||
- XState store
|
- XState store
|
||||||
@@ -26,15 +26,14 @@ description: Sync this Next.js frontend with backend API documentation
|
|||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| API Path | `src/data/services/api/api_path.ts` | 统一维护接口路径 |
|
| API Path | `src/data/services/api/api_path.ts` | 统一维护接口路径 |
|
||||||
| API Service | `src/data/services/api/*_api.ts` | 调用 `httpClient` 并解析响应 |
|
| API Service | `src/data/services/api/*_api.ts` | 调用 `httpClient` 并解析响应 |
|
||||||
| Schema | `src/data/schemas/<module>/*.ts` | Zod schema,负责防御性解析 |
|
| Schema Model | `src/data/schemas/<module>/*.ts` | Zod schema 与不可变数据类,负责解析和序列化 |
|
||||||
| DTO | `src/data/dto/<module>/*.ts` | DTO class,封装 `fromJson/toJson` |
|
|
||||||
| Repository Interface | `src/data/repositories/interfaces/i*_repository.ts` | 仓库接口 |
|
| Repository Interface | `src/data/repositories/interfaces/i*_repository.ts` | 仓库接口 |
|
||||||
| Repository | `src/data/repositories/*_repository.ts` | 业务仓库实现 |
|
| Repository | `src/data/repositories/*_repository.ts` | 业务仓库实现 |
|
||||||
| Storage | `src/data/storage/*` | 本地持久化 |
|
| Storage | `src/data/storage/*` | 本地持久化 |
|
||||||
| Store | `src/stores/<module>/*` | XState 状态机、actors、helpers、sync |
|
| Store | `src/stores/<module>/*` | XState 状态机、actors、helpers、sync |
|
||||||
| UI | `src/app/**` | Next.js app router UI |
|
| UI | `src/app/**` | Next.js app router UI |
|
||||||
| Mock | `src/data/mock/<module>/**` | 模拟请求 / 响应数据 |
|
| Mock | `src/data/mock/<module>/**` | 模拟请求 / 响应数据 |
|
||||||
| Tests | `**/__tests__/*.test.ts` | DTO、helper、machine transition 测试 |
|
| Tests | `**/__tests__/*.test.ts` | schema model、helper、machine transition 测试 |
|
||||||
|
|
||||||
## 后端文档来源
|
## 后端文档来源
|
||||||
|
|
||||||
@@ -108,7 +107,7 @@ src/data/services/api/api_path.ts
|
|||||||
static readonly userEntitlements = `${ApiPath._user}/entitlements`;
|
static readonly userEntitlements = `${ApiPath._user}/entitlements`;
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. 更新 Schema
|
### 4. 更新 Schema Model
|
||||||
|
|
||||||
在对应模块下新增或修改:
|
在对应模块下新增或修改:
|
||||||
|
|
||||||
@@ -123,6 +122,13 @@ src/data/schemas/<module>/*.ts
|
|||||||
- 输入类型用 `z.input<typeof Schema>`。
|
- 输入类型用 `z.input<typeof Schema>`。
|
||||||
- 对后端可能返回 `null` 的字段做防御性处理。
|
- 对后端可能返回 `null` 的字段做防御性处理。
|
||||||
- 不要在 schema 中写 UI 逻辑。
|
- 不要在 schema 中写 UI 逻辑。
|
||||||
|
- Schema 与对应不可变数据类必须定义在同一个文件中。
|
||||||
|
- 数据类的 `declare readonly` 只声明业务代码直接访问的字段。
|
||||||
|
- 后端返回但前端既不读取、也不透传的字段,不要加入 Schema。
|
||||||
|
- 仅用于请求序列化、响应兼容或内部透传的字段保留在 Schema 中,不声明 class 属性。
|
||||||
|
- 数据类统一使用 `from()`、`fromJson()`、`toJson()` 与 `Object.freeze(this)`。
|
||||||
|
- 没有嵌套转换或自定义方法时,使用 `src/data/schemas/schema_model.ts` 的 `createSchemaModel()`。
|
||||||
|
- 不要再创建独立的数据传输对象层。
|
||||||
|
|
||||||
示例:
|
示例:
|
||||||
|
|
||||||
@@ -134,36 +140,8 @@ export const XxxResponseSchema = z.object({
|
|||||||
|
|
||||||
export type XxxResponseInput = z.input<typeof XxxResponseSchema>;
|
export type XxxResponseInput = z.input<typeof XxxResponseSchema>;
|
||||||
export type XxxResponseData = z.output<typeof XxxResponseSchema>;
|
export type XxxResponseData = z.output<typeof XxxResponseSchema>;
|
||||||
```
|
|
||||||
|
|
||||||
### 5. 更新 DTO
|
|
||||||
|
|
||||||
在对应模块下新增或修改:
|
|
||||||
|
|
||||||
```text
|
|
||||||
src/data/dto/<module>/*.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
要求:
|
|
||||||
|
|
||||||
- DTO class 的 `declare readonly` 只声明前端业务代码会直接访问的字段。
|
|
||||||
- 不要因为后端响应包含某字段,就机械地为它增加 class 属性、嵌套类型或类型别名。
|
|
||||||
- 后端返回但前端既不读取、也不透传的字段,可以不加入 schema 和 DTO。
|
|
||||||
- 仅用于请求序列化、响应兼容或内部透传的字段保留在 schema 中,不添加 class 属性声明。
|
|
||||||
- `Object.assign(this, data)` 会保留 schema 解析后的字段;即使 class 未声明对应属性,`toJson()` 仍可按 schema 正常序列化。
|
|
||||||
- 新增 DTO 字段前先搜索实际调用点;没有直接读取方时默认不声明。
|
|
||||||
|
|
||||||
DTO 统一模式:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export const XxxResponseSchema = z.object({
|
|
||||||
id: z.string(),
|
|
||||||
// 仅用于内部透传,不需要在 DTO class 中声明。
|
|
||||||
traceId: z.string(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export class XxxResponse {
|
export class XxxResponse {
|
||||||
// 业务代码直接读取的字段才公开声明。
|
|
||||||
declare readonly id: string;
|
declare readonly id: string;
|
||||||
|
|
||||||
private constructor(input: XxxResponseInput) {
|
private constructor(input: XxxResponseInput) {
|
||||||
@@ -186,7 +164,7 @@ export class XxxResponse {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 6. 更新 API Service
|
### 5. 更新 API Service
|
||||||
|
|
||||||
修改:
|
修改:
|
||||||
|
|
||||||
@@ -210,7 +188,7 @@ return XxxResponse.fromJson(unwrap(env) as Record<string, unknown>);
|
|||||||
- 不要在 API 层处理 UI 行为。
|
- 不要在 API 层处理 UI 行为。
|
||||||
- multipart 仍使用 `FormData`。
|
- multipart 仍使用 `FormData`。
|
||||||
|
|
||||||
### 7. 更新 Repository
|
### 6. 更新 Repository
|
||||||
|
|
||||||
修改:
|
修改:
|
||||||
|
|
||||||
@@ -229,14 +207,14 @@ async getXxx(): Promise<Result<XxxResponse>> {
|
|||||||
|
|
||||||
Repository 可以做轻量编排,例如:
|
Repository 可以做轻量编排,例如:
|
||||||
|
|
||||||
- request DTO 构造
|
- request 数据类构造
|
||||||
- 多接口组合
|
- 多接口组合
|
||||||
- 本地 storage 同步
|
- 本地 storage 同步
|
||||||
- local model 与 DTO 转换
|
- 本地模型与接口模型转换
|
||||||
|
|
||||||
不要把 React / UI 逻辑放进 repository。
|
不要把 React / UI 逻辑放进 repository。
|
||||||
|
|
||||||
### 8. 更新 Storage
|
### 7. 更新 Storage
|
||||||
|
|
||||||
如果接口影响本地持久化,修改:
|
如果接口影响本地持久化,修改:
|
||||||
|
|
||||||
@@ -262,7 +240,7 @@ getEntitlementSnapshot(): Promise<Result<UserEntitlementSnapshotData | null>> {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 9. 更新 Store / 状态机
|
### 8. 更新 Store / 状态机
|
||||||
|
|
||||||
如果接口影响状态流,修改:
|
如果接口影响状态流,修改:
|
||||||
|
|
||||||
@@ -284,7 +262,7 @@ src/stores/<module>/*-sync.tsx
|
|||||||
- 页面组件不承担全局监听职责。
|
- 页面组件不承担全局监听职责。
|
||||||
- 状态机事件应表达业务事实,而不是 UI 操作细节。
|
- 状态机事件应表达业务事实,而不是 UI 操作细节。
|
||||||
|
|
||||||
### 10. 更新 UI
|
### 9. 更新 UI
|
||||||
|
|
||||||
只有在接口变更影响展示或用户明确要求时,才修改:
|
只有在接口变更影响展示或用户明确要求时,才修改:
|
||||||
|
|
||||||
@@ -298,19 +276,18 @@ src/app/**
|
|||||||
- 不在页面组件里直接写复杂业务编排。
|
- 不在页面组件里直接写复杂业务编排。
|
||||||
- 全局监听逻辑优先放到 `stores/*/*-sync.tsx`。
|
- 全局监听逻辑优先放到 `stores/*/*-sync.tsx`。
|
||||||
|
|
||||||
### 11. 更新桶文件
|
### 10. 更新桶文件
|
||||||
|
|
||||||
如果新增 schema / DTO / component,更新对应 `index.ts`:
|
如果新增 schema model 或 component,更新对应 `index.ts`:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
src/data/schemas/<module>/index.ts
|
src/data/schemas/<module>/index.ts
|
||||||
src/data/dto/<module>/index.ts
|
|
||||||
src/app/**/components/index.ts
|
src/app/**/components/index.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
如果项目使用 barrelsby 生成,不要手动破坏现有导出格式。
|
如果项目使用 barrelsby 生成,不要手动破坏现有导出格式。
|
||||||
|
|
||||||
### 12. 更新 Mock 数据
|
### 11. 更新 Mock 数据
|
||||||
|
|
||||||
如果新增或调整数据结构,按一个结构一个文件的原则放到:
|
如果新增或调整数据结构,按一个结构一个文件的原则放到:
|
||||||
|
|
||||||
@@ -322,11 +299,11 @@ src/data/mock/<module>/websocket-events
|
|||||||
|
|
||||||
不要把多个结构塞进一个大 JSON。
|
不要把多个结构塞进一个大 JSON。
|
||||||
|
|
||||||
### 13. 更新测试
|
### 12. 更新测试
|
||||||
|
|
||||||
优先补充:
|
优先补充:
|
||||||
|
|
||||||
- DTO / schema parse 测试
|
- schema model 解析与序列化测试
|
||||||
- helper 纯函数测试
|
- helper 纯函数测试
|
||||||
- XState transition 测试
|
- XState transition 测试
|
||||||
- repository 编排测试(如已有测试基础)
|
- repository 编排测试(如已有测试基础)
|
||||||
@@ -334,11 +311,11 @@ src/data/mock/<module>/websocket-events
|
|||||||
常见位置:
|
常见位置:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
src/data/dto/<module>/__tests__/*.test.ts
|
src/data/schemas/<module>/__tests__/*.test.ts
|
||||||
src/stores/<module>/__tests__/*.test.ts
|
src/stores/<module>/__tests__/*.test.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
### 14. 验证
|
### 13. 验证
|
||||||
|
|
||||||
至少运行:
|
至少运行:
|
||||||
|
|
||||||
@@ -356,18 +333,17 @@ pnpm exec vitest run <related tests>
|
|||||||
|
|
||||||
1. 文档确认 path / method / payload。
|
1. 文档确认 path / method / payload。
|
||||||
2. `api_path.ts` 新增路径。
|
2. `api_path.ts` 新增路径。
|
||||||
3. 新增 schema。
|
3. 在同一文件新增 schema 与不可变数据类。
|
||||||
4. 新增 DTO。
|
4. API service 新增方法。
|
||||||
5. API service 新增方法。
|
5. repository interface + implementation 新增方法。
|
||||||
6. repository interface + implementation 新增方法。
|
6. 如需要,接入 store actor / event。
|
||||||
7. 如需要,接入 store actor / event。
|
7. 补 mock 和测试。
|
||||||
8. 补 mock 和测试。
|
8. 运行验证。
|
||||||
9. 运行验证。
|
|
||||||
|
|
||||||
### 修改响应字段
|
### 修改响应字段
|
||||||
|
|
||||||
1. 更新 schema。
|
1. 更新 schema。
|
||||||
2. 更新 DTO declare 字段和 `toJson`。
|
2. 更新同文件数据类的公开字段和 `toJson`。
|
||||||
3. 更新 mapper/helper。
|
3. 更新 mapper/helper。
|
||||||
4. 更新 UI 或状态机依赖字段。
|
4. 更新 UI 或状态机依赖字段。
|
||||||
5. 更新 mock 和测试。
|
5. 更新 mock 和测试。
|
||||||
@@ -377,7 +353,7 @@ pnpm exec vitest run <related tests>
|
|||||||
|
|
||||||
1. 搜索所有引用。
|
1. 搜索所有引用。
|
||||||
2. 删除 API path / API method / repository method。
|
2. 删除 API path / API method / repository method。
|
||||||
3. 删除 DTO/schema/mock/test。
|
3. 删除 schema model、mock 和测试。
|
||||||
4. 更新状态机和 UI 调用。
|
4. 更新状态机和 UI 调用。
|
||||||
5. 运行 `rg` 确认无残留引用。
|
5. 运行 `rg` 确认无残留引用。
|
||||||
6. 运行验证。
|
6. 运行验证。
|
||||||
|
|||||||
@@ -12,13 +12,6 @@
|
|||||||
"./src/data/repositories",
|
"./src/data/repositories",
|
||||||
"./src/data/repositories/interfaces",
|
"./src/data/repositories/interfaces",
|
||||||
"./src/data/services/api",
|
"./src/data/services/api",
|
||||||
"./src/data/dto/auth",
|
|
||||||
"./src/data/dto/chat",
|
|
||||||
"./src/data/dto/character",
|
|
||||||
"./src/data/dto/feedback",
|
|
||||||
"./src/data/dto/feedback/response",
|
|
||||||
"./src/data/dto/metrics",
|
|
||||||
"./src/data/dto/user",
|
|
||||||
"./src/data/schemas/auth",
|
"./src/data/schemas/auth",
|
||||||
"./src/data/schemas/auth/request",
|
"./src/data/schemas/auth/request",
|
||||||
"./src/data/schemas/auth/response",
|
"./src/data/schemas/auth/response",
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ BACKEND_OPENAPI_SOURCE=https://backend.example.com/openapi.json pnpm contract:ch
|
|||||||
|
|
||||||
检查会验证前端依赖的每个 method/path 是否仍由后端公开。路径参数名称不同
|
检查会验证前端依赖的每个 method/path 是否仍由后端公开。路径参数名称不同
|
||||||
(例如 `{albumId}` 与 `{album_id}`)不会产生误报。字段级兼容仍由前端 Zod
|
(例如 `{albumId}` 与 `{album_id}`)不会产生误报。字段级兼容仍由前端 Zod
|
||||||
Schema 和 DTO 测试负责,避免仅凭 OpenAPI 生成代码覆盖现有防御性解析规则。
|
Schema Model 测试负责,避免仅凭 OpenAPI 生成代码覆盖现有防御性解析规则。
|
||||||
|
|
||||||
## CI 配置
|
## CI 配置
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { act } from "react";
|
|||||||
import { createRoot, type Root } from "react-dom/client";
|
import { createRoot, type Root } from "react-dom/client";
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
import { PaymentPlan } from "@/data/dto/payment";
|
import { PaymentPlan } from "@/data/schemas/payment";
|
||||||
import { behaviorAnalytics } from "@/lib/analytics";
|
import { behaviorAnalytics } from "@/lib/analytics";
|
||||||
|
|
||||||
import { usePaymentPlanAnalytics } from "../use-payment-plan-analytics";
|
import { usePaymentPlanAnalytics } from "../use-payment-plan-analytics";
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
import type { PaymentPlan } from "@/data/dto/payment";
|
import type { PaymentPlan } from "@/data/schemas/payment";
|
||||||
import {
|
import {
|
||||||
behaviorAnalytics,
|
behaviorAnalytics,
|
||||||
type PaymentAnalyticsContext,
|
type PaymentAnalyticsContext,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { type Dispatch, useEffect, useRef } from "react";
|
import { type Dispatch, useEffect, useRef } from "react";
|
||||||
|
|
||||||
import type { PayChannel } from "@/data/dto/payment";
|
import type { PayChannel } from "@/data/schemas/payment";
|
||||||
import type { PendingPaymentSubscriptionType } from "@/lib/payment/pending_payment_order";
|
import type { PendingPaymentSubscriptionType } from "@/lib/payment/pending_payment_order";
|
||||||
import {
|
import {
|
||||||
usePaymentDispatch,
|
usePaymentDispatch,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
import type { UiMessage } from "@/data/dto/chat";
|
import type { UiMessage } from "@/stores/chat/ui-message";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
buildChatRenderItems,
|
buildChatRenderItems,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { UiMessage } from "@/data/dto/chat";
|
import type { UiMessage } from "@/stores/chat/ui-message";
|
||||||
|
|
||||||
export type ChatMessageKeyResolver = (message: UiMessage) => string;
|
export type ChatMessageKeyResolver = (message: UiMessage) => string;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import type { LoginStatus } from "@/data/dto/auth";
|
import type { LoginStatus } from "@/data/schemas/auth";
|
||||||
import type { ChatUpgradeReason } from "@/stores/chat/chat-state";
|
import type { ChatUpgradeReason } from "@/stores/chat/chat-state";
|
||||||
import { AppEnvUtil } from "@/utils/app-env";
|
import { AppEnvUtil } from "@/utils/app-env";
|
||||||
import { BrowserDetector } from "@/utils/browser-detect";
|
import { BrowserDetector } from "@/utils/browser-detect";
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { act } from "react";
|
|||||||
import { createRoot, type Root } from "react-dom/client";
|
import { createRoot, type Root } from "react-dom/client";
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
import type { UiMessage } from "@/data/dto/chat";
|
import type { UiMessage } from "@/stores/chat/ui-message";
|
||||||
|
|
||||||
import { ChatArea } from "../chat-area";
|
import { ChatArea } from "../chat-area";
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import {
|
|||||||
} from "react";
|
} from "react";
|
||||||
import { LoaderCircle } from "lucide-react";
|
import { LoaderCircle } from "lucide-react";
|
||||||
|
|
||||||
import type { UiMessage } from "@/data/dto/chat";
|
import type { UiMessage } from "@/stores/chat/ui-message";
|
||||||
import { DEFAULT_CHARACTER_ID } from "@/data/constants/character";
|
import { DEFAULT_CHARACTER_ID } from "@/data/constants/character";
|
||||||
import { usePullToRefresh } from "@/hooks/use-pull-to-refresh";
|
import { usePullToRefresh } from "@/hooks/use-pull-to-refresh";
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { shallowEqual } from "@xstate/react";
|
import { shallowEqual } from "@xstate/react";
|
||||||
|
|
||||||
import type { LoginStatus } from "@/data/dto/auth";
|
import type { LoginStatus } from "@/data/schemas/auth";
|
||||||
import { useAppNavigator } from "@/router/use-app-navigator";
|
import { useAppNavigator } from "@/router/use-app-navigator";
|
||||||
import {
|
import {
|
||||||
usePaymentDispatch,
|
usePaymentDispatch,
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
import type { LoginStatus } from "@/data/dto/auth";
|
import type { LoginStatus } from "@/data/schemas/auth";
|
||||||
import type { UiMessage } from "@/data/dto/chat";
|
import type { UiMessage } from "@/stores/chat/ui-message";
|
||||||
import { resolveSplashLatestMessageCacheIdentity } from "@/lib/chat/splash_latest_message";
|
import { resolveSplashLatestMessageCacheIdentity } from "@/lib/chat/splash_latest_message";
|
||||||
import { getLatestSplashMessagePreview } from "@/lib/chat/splash_latest_message_preview";
|
import { getLatestSplashMessagePreview } from "@/lib/chat/splash_latest_message_preview";
|
||||||
import { useSplashLatestMessageCache } from "@/providers/splash-latest-message-provider";
|
import { useSplashLatestMessageCache } from "@/providers/splash-latest-message-provider";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import packageInfo from "../../../package.json";
|
import packageInfo from "../../../package.json";
|
||||||
|
|
||||||
import type { FeedbackContext } from "@/data/dto/feedback";
|
import type { FeedbackContext } from "@/data/schemas/feedback";
|
||||||
import { BrowserDetector } from "@/utils/browser-detect";
|
import { BrowserDetector } from "@/utils/browser-detect";
|
||||||
import { PlatformDetector } from "@/utils/platform-detect";
|
import { PlatformDetector } from "@/utils/platform-detect";
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
|
|
||||||
import { BackButton } from "@/app/_components";
|
import { BackButton } from "@/app/_components";
|
||||||
import { MobileShell } from "@/app/_components/core";
|
import { MobileShell } from "@/app/_components/core";
|
||||||
import type { FeedbackCategory } from "@/data/dto/feedback";
|
import type { FeedbackCategory } from "@/data/schemas/feedback";
|
||||||
import { ROUTES } from "@/router/routes";
|
import { ROUTES } from "@/router/routes";
|
||||||
import { useAppNavigator } from "@/router/use-app-navigator";
|
import { useAppNavigator } from "@/router/use-app-navigator";
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { type FormEvent, useEffect, useRef, useState } from "react";
|
import { type FormEvent, useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
import type { FeedbackCategory } from "@/data/dto/feedback";
|
import type { FeedbackCategory } from "@/data/schemas/feedback";
|
||||||
import { submitFeedback } from "@/lib/feedback";
|
import { submitFeedback } from "@/lib/feedback";
|
||||||
|
|
||||||
import { createFeedbackContext } from "./feedback-context";
|
import { createFeedbackContext } from "./feedback-context";
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { act, type Dispatch } from "react";
|
|||||||
import { createRoot, type Root } from "react-dom/client";
|
import { createRoot, type Root } from "react-dom/client";
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
import type { LoginStatus } from "@/data/dto/auth";
|
import type { LoginStatus } from "@/data/schemas/auth";
|
||||||
import { ROUTES } from "@/router/routes";
|
import { ROUTES } from "@/router/routes";
|
||||||
import type { PrivateRoomEvent } from "@/stores/private-room";
|
import type { PrivateRoomEvent } from "@/stores/private-room";
|
||||||
import type { PrivateRoomUnlockPaywallRequest } from "@/stores/private-room/private-room-state";
|
import type { PrivateRoomUnlockPaywallRequest } from "@/stores/private-room/private-room-state";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { renderToStaticMarkup } from "react-dom/server";
|
import { renderToStaticMarkup } from "react-dom/server";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
import { PrivateAlbum } from "@/data/dto/private-room";
|
import { PrivateAlbum } from "@/data/schemas/private-room";
|
||||||
|
|
||||||
import { PrivateAlbumCard } from "../private-album-card";
|
import { PrivateAlbumCard } from "../private-album-card";
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import Image from "next/image";
|
|||||||
import { ImageIcon, LockKeyhole } from "lucide-react";
|
import { ImageIcon, LockKeyhole } from "lucide-react";
|
||||||
|
|
||||||
import { CharacterAvatar } from "@/app/_components";
|
import { CharacterAvatar } from "@/app/_components";
|
||||||
import type { PrivateAlbum } from "@/data/dto/private-room";
|
import type { PrivateAlbum } from "@/data/schemas/private-room";
|
||||||
import { isPrivateAlbumLocked } from "@/lib/private-room/private_album";
|
import { isPrivateAlbumLocked } from "@/lib/private-room/private_album";
|
||||||
|
|
||||||
import styles from "../private-room-screen.module.css";
|
import styles from "../private-room-screen.module.css";
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useEffect, useRef } from "react";
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { ChevronLeft, ChevronRight, X } from "lucide-react";
|
import { ChevronLeft, ChevronRight, X } from "lucide-react";
|
||||||
|
|
||||||
import type { PrivateAlbum } from "@/data/dto/private-room";
|
import type { PrivateAlbum } from "@/data/schemas/private-room";
|
||||||
|
|
||||||
import styles from "../private-room-screen.module.css";
|
import styles from "../private-room-screen.module.css";
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { PrivateAlbum } from "@/data/dto/private-room";
|
import type { PrivateAlbum } from "@/data/schemas/private-room";
|
||||||
|
|
||||||
import styles from "../private-room-screen.module.css";
|
import styles from "../private-room-screen.module.css";
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { type Dispatch, useEffect, useRef } from "react";
|
import { type Dispatch, useEffect, useRef } from "react";
|
||||||
|
|
||||||
import type { LoginStatus } from "@/data/dto/auth";
|
import type { LoginStatus } from "@/data/schemas/auth";
|
||||||
import { useGuestLoginBootstrap } from "@/hooks/use-guest-login-bootstrap";
|
import { useGuestLoginBootstrap } from "@/hooks/use-guest-login-bootstrap";
|
||||||
import { behaviorAnalytics } from "@/lib/analytics";
|
import { behaviorAnalytics } from "@/lib/analytics";
|
||||||
import { ROUTES } from "@/router/routes";
|
import { ROUTES } from "@/router/routes";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
import type { UserView } from "@/data/dto/user";
|
import type { UserView } from "@/stores/user/user-view";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getSidebarViewModel,
|
getSidebarViewModel,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { LoginStatus } from "@/data/dto/auth";
|
import type { LoginStatus } from "@/data/schemas/auth";
|
||||||
import type { UserView } from "@/data/dto/user";
|
import type { UserView } from "@/stores/user/user-view";
|
||||||
|
|
||||||
export type SidebarUserState = "guest" | "member" | "vip";
|
export type SidebarUserState = "guest" | "member" | "vip";
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { type Dispatch, useEffect } from "react";
|
import { type Dispatch, useEffect } from "react";
|
||||||
|
|
||||||
import type { LoginStatus } from "@/data/dto/auth";
|
import type { LoginStatus } from "@/data/schemas/auth";
|
||||||
|
|
||||||
interface SidebarAuthBootstrapState {
|
interface SidebarAuthBootstrapState {
|
||||||
hasInitialized: boolean;
|
hasInitialized: boolean;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import type { LoginStatus } from "@/data/dto/auth";
|
import type { LoginStatus } from "@/data/schemas/auth";
|
||||||
import { useHasHydrated } from "@/hooks/use-has-hydrated";
|
import { useHasHydrated } from "@/hooks/use-has-hydrated";
|
||||||
import { loadSplashLatestMessagePreview } from "@/lib/chat/splash_latest_message";
|
import { loadSplashLatestMessagePreview } from "@/lib/chat/splash_latest_message";
|
||||||
import { useSplashLatestMessageCache } from "@/providers/splash-latest-message-provider";
|
import { useSplashLatestMessageCache } from "@/providers/splash-latest-message-provider";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
import { PaymentPlan } from "@/data/dto/payment";
|
import { PaymentPlan } from "@/data/schemas/payment";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
canChooseSubscriptionPayChannel,
|
canChooseSubscriptionPayChannel,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
import type { PayChannel } from "@/data/dto/payment";
|
import type { PayChannel } from "@/data/schemas/payment";
|
||||||
|
|
||||||
const PAYMENT_METHODS: {
|
const PAYMENT_METHODS: {
|
||||||
channel: PayChannel;
|
channel: PayChannel;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { PayChannel, PaymentPlan } from "@/data/dto/payment";
|
import type { PayChannel, PaymentPlan } from "@/data/schemas/payment";
|
||||||
import { getDefaultPayChannelForCountryCode } from "@/lib/payment/default_pay_channel";
|
import { getDefaultPayChannelForCountryCode } from "@/lib/payment/default_pay_channel";
|
||||||
import { AppEnvUtil } from "@/utils/app-env";
|
import { AppEnvUtil } from "@/utils/app-env";
|
||||||
export { getDefaultPayChannelForCountryCode } from "@/lib/payment/default_pay_channel";
|
export { getDefaultPayChannelForCountryCode } from "@/lib/payment/default_pay_channel";
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { BackButton } from "@/app/_components";
|
|||||||
import { Checkbox, MobileShell } from "@/app/_components/core";
|
import { Checkbox, MobileShell } from "@/app/_components/core";
|
||||||
import { usePaymentPlanAnalytics } from "@/app/_hooks/use-payment-plan-analytics";
|
import { usePaymentPlanAnalytics } from "@/app/_hooks/use-payment-plan-analytics";
|
||||||
import { AppConstants } from "@/core/app_constants";
|
import { AppConstants } from "@/core/app_constants";
|
||||||
import type { PayChannel } from "@/data/dto/payment";
|
import type { PayChannel } from "@/data/schemas/payment";
|
||||||
import type { SubscriptionReturnTo } from "@/lib/navigation/subscription_exit";
|
import type { SubscriptionReturnTo } from "@/lib/navigation/subscription_exit";
|
||||||
import {
|
import {
|
||||||
behaviorAnalytics,
|
behaviorAnalytics,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useEffect, useRef, useState } from "react";
|
|||||||
|
|
||||||
import { usePaymentRouteFlow } from "@/app/_hooks/use-payment-route-flow";
|
import { usePaymentRouteFlow } from "@/app/_hooks/use-payment-route-flow";
|
||||||
import { useAppNavigator } from "@/router/use-app-navigator";
|
import { useAppNavigator } from "@/router/use-app-navigator";
|
||||||
import type { PayChannel } from "@/data/dto/payment";
|
import type { PayChannel } from "@/data/schemas/payment";
|
||||||
import type { SubscriptionReturnTo } from "@/lib/navigation/subscription_exit";
|
import type { SubscriptionReturnTo } from "@/lib/navigation/subscription_exit";
|
||||||
|
|
||||||
import type { SubscriptionType } from "./subscription-screen.helpers";
|
import type { SubscriptionType } from "./subscription-screen.helpers";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
import { PaymentPlan } from "@/data/dto/payment";
|
import { PaymentPlan } from "@/data/schemas/payment";
|
||||||
import type { PaymentPlanInput } from "@/data/schemas/payment/payment_plan";
|
import type { PaymentPlanInput } from "@/data/schemas/payment/payment_plan";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { LoginStatus } from "@/data/dto/auth";
|
import type { LoginStatus } from "@/data/schemas/auth";
|
||||||
import type { PaymentPlan } from "@/data/dto/payment";
|
import type { PaymentPlan } from "@/data/schemas/payment";
|
||||||
import {
|
import {
|
||||||
getTipCoffeeOption,
|
getTipCoffeeOption,
|
||||||
type TipCoffeeType,
|
type TipCoffeeType,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { CharacterAvatar } from "@/app/_components";
|
|||||||
import { MobileShell } from "@/app/_components/core";
|
import { MobileShell } from "@/app/_components/core";
|
||||||
import { usePaymentPlanAnalytics } from "@/app/_hooks/use-payment-plan-analytics";
|
import { usePaymentPlanAnalytics } from "@/app/_hooks/use-payment-plan-analytics";
|
||||||
import { usePaymentRouteFlow } from "@/app/_hooks/use-payment-route-flow";
|
import { usePaymentRouteFlow } from "@/app/_hooks/use-payment-route-flow";
|
||||||
import type { PayChannel } from "@/data/dto/payment";
|
import type { PayChannel } from "@/data/schemas/payment";
|
||||||
import {
|
import {
|
||||||
behaviorAnalytics,
|
behaviorAnalytics,
|
||||||
type PaymentAnalyticsContext,
|
type PaymentAnalyticsContext,
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { createSchemaDto, type SchemaDto } from "../schema_dto";
|
|
||||||
|
|
||||||
const ExampleSchema = z.object({
|
|
||||||
id: z.string(),
|
|
||||||
traceId: z.string().default("generated-trace"),
|
|
||||||
});
|
|
||||||
|
|
||||||
type ExampleDto = SchemaDto<typeof ExampleSchema, "id">;
|
|
||||||
const ExampleDto = createSchemaDto<typeof ExampleSchema, "id">(
|
|
||||||
ExampleSchema,
|
|
||||||
);
|
|
||||||
|
|
||||||
describe("createSchemaDto", () => {
|
|
||||||
it("parses, freezes and serializes through the source schema", () => {
|
|
||||||
const dto: ExampleDto = ExampleDto.from({ id: "item-1" });
|
|
||||||
|
|
||||||
expect(dto).toBeInstanceOf(ExampleDto);
|
|
||||||
expect(Object.isFrozen(dto)).toBe(true);
|
|
||||||
expect(dto.id).toBe("item-1");
|
|
||||||
expect(dto.toJson()).toEqual({
|
|
||||||
id: "item-1",
|
|
||||||
traceId: "generated-trace",
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("keeps non-public schema fields out of the declared DTO interface", () => {
|
|
||||||
const dto = ExampleDto.fromJson({ id: "item-1", traceId: "trace-1" });
|
|
||||||
|
|
||||||
// @ts-expect-error traceId is serialized but not part of the public DTO API.
|
|
||||||
expect(dto.traceId).toBe("trace-1");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("preserves schema validation errors", () => {
|
|
||||||
expect(() => ExampleDto.fromJson({ id: 1 })).toThrow(z.ZodError);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
/**
|
|
||||||
* Facebook 用户数据 DTO
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
FacebookUserDataSchema,
|
|
||||||
type FacebookUserDataInput,
|
|
||||||
type FacebookUserDataData,
|
|
||||||
} from "@/data/schemas/auth/facebook_user_data";
|
|
||||||
|
|
||||||
export class FacebookUserData {
|
|
||||||
declare readonly id: string | null;
|
|
||||||
declare readonly name: string | null;
|
|
||||||
declare readonly email: string | null;
|
|
||||||
declare readonly pictureUrl: string | null;
|
|
||||||
|
|
||||||
private constructor(input: FacebookUserDataInput) {
|
|
||||||
const data = FacebookUserDataSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: FacebookUserDataInput): FacebookUserData {
|
|
||||||
return new FacebookUserData(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): FacebookUserData {
|
|
||||||
return FacebookUserData.from(json as FacebookUserDataInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): FacebookUserDataData {
|
|
||||||
return FacebookUserDataSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file Automatically generated by barrelsby.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export * from "./auth_panel_mode";
|
|
||||||
export * from "./facebook_user_data";
|
|
||||||
export * from "./login_status";
|
|
||||||
export * from "./request";
|
|
||||||
export * from "./response";
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
/**
|
|
||||||
* Facebook identity 绑定请求 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
FacebookIdentityRequestSchema,
|
|
||||||
} from "@/data/schemas/auth/request/facebook_identity_request";
|
|
||||||
import {
|
|
||||||
createSchemaDto,
|
|
||||||
type SchemaDto,
|
|
||||||
} from "@/data/dto/schema_dto";
|
|
||||||
|
|
||||||
export type FacebookIdentityRequest = SchemaDto<
|
|
||||||
typeof FacebookIdentityRequestSchema
|
|
||||||
>;
|
|
||||||
export const FacebookIdentityRequest = createSchemaDto(
|
|
||||||
FacebookIdentityRequestSchema,
|
|
||||||
);
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/**
|
|
||||||
* Facebook 登录请求 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
FacebookLoginRequestSchema,
|
|
||||||
type FacebookLoginRequestInput,
|
|
||||||
type FacebookLoginRequestData,
|
|
||||||
} from "@/data/schemas/auth/request/facebook_login_request";
|
|
||||||
|
|
||||||
export class FacebookLoginRequest {
|
|
||||||
declare readonly accessToken: string;
|
|
||||||
declare readonly platform: string;
|
|
||||||
declare readonly guestId: string;
|
|
||||||
declare readonly isTestAccount: boolean;
|
|
||||||
|
|
||||||
private constructor(input: FacebookLoginRequestInput) {
|
|
||||||
const data = FacebookLoginRequestSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: FacebookLoginRequestInput): FacebookLoginRequest {
|
|
||||||
return new FacebookLoginRequest(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): FacebookLoginRequest {
|
|
||||||
return FacebookLoginRequest.from(json as FacebookLoginRequestInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): FacebookLoginRequestData {
|
|
||||||
return FacebookLoginRequestSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
/**
|
|
||||||
* Facebook PSID 登录请求 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
FacebookPsidLoginRequestSchema,
|
|
||||||
} from "@/data/schemas/auth/request/facebook_psid_login_request";
|
|
||||||
import {
|
|
||||||
createSchemaDto,
|
|
||||||
type SchemaDto,
|
|
||||||
} from "@/data/dto/schema_dto";
|
|
||||||
|
|
||||||
export type FacebookPsidLoginRequest = SchemaDto<
|
|
||||||
typeof FacebookPsidLoginRequestSchema
|
|
||||||
>;
|
|
||||||
export const FacebookPsidLoginRequest = createSchemaDto(
|
|
||||||
FacebookPsidLoginRequestSchema,
|
|
||||||
);
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
/**
|
|
||||||
* Facebook ID 登录请求 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
FbIdLoginRequestSchema,
|
|
||||||
type FbIdLoginRequestInput,
|
|
||||||
type FbIdLoginRequestData,
|
|
||||||
} from "@/data/schemas/auth/request/fb_id_login_request";
|
|
||||||
|
|
||||||
export class FbIdLoginRequest {
|
|
||||||
declare readonly fbId: string;
|
|
||||||
declare readonly avatarUrl: string;
|
|
||||||
declare readonly isTestAccount: boolean;
|
|
||||||
|
|
||||||
private constructor(input: FbIdLoginRequestInput) {
|
|
||||||
const data = FbIdLoginRequestSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: FbIdLoginRequestInput): FbIdLoginRequest {
|
|
||||||
return new FbIdLoginRequest(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): FbIdLoginRequest {
|
|
||||||
return FbIdLoginRequest.from(json as FbIdLoginRequestInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): FbIdLoginRequestData {
|
|
||||||
return FbIdLoginRequestSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/**
|
|
||||||
* Google 登录请求 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
GoogleLoginRequestSchema,
|
|
||||||
type GoogleLoginRequestInput,
|
|
||||||
type GoogleLoginRequestData,
|
|
||||||
} from "@/data/schemas/auth/request/google_login_request";
|
|
||||||
|
|
||||||
export class GoogleLoginRequest {
|
|
||||||
declare readonly idToken: string;
|
|
||||||
declare readonly platform: string;
|
|
||||||
declare readonly guestId: string;
|
|
||||||
declare readonly isTestAccount: boolean;
|
|
||||||
|
|
||||||
private constructor(input: GoogleLoginRequestInput) {
|
|
||||||
const data = GoogleLoginRequestSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: GoogleLoginRequestInput): GoogleLoginRequest {
|
|
||||||
return new GoogleLoginRequest(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): GoogleLoginRequest {
|
|
||||||
return GoogleLoginRequest.from(json as GoogleLoginRequestInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): GoogleLoginRequestData {
|
|
||||||
return GoogleLoginRequestSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
/**
|
|
||||||
* 游客登录请求 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
GuestLoginRequestSchema,
|
|
||||||
type GuestLoginRequestInput,
|
|
||||||
type GuestLoginRequestData,
|
|
||||||
} from "@/data/schemas/auth/request/guest_login_request";
|
|
||||||
|
|
||||||
export class GuestLoginRequest {
|
|
||||||
declare readonly deviceId: string;
|
|
||||||
declare readonly isTestAccount: boolean;
|
|
||||||
|
|
||||||
private constructor(input: GuestLoginRequestInput) {
|
|
||||||
const data = GuestLoginRequestSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: GuestLoginRequestInput): GuestLoginRequest {
|
|
||||||
return new GuestLoginRequest(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): GuestLoginRequest {
|
|
||||||
return GuestLoginRequest.from(json as GuestLoginRequestInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): GuestLoginRequestData {
|
|
||||||
return GuestLoginRequestSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
export * from "./facebook_identity_request";
|
|
||||||
export * from "./facebook_login_request";
|
|
||||||
export * from "./facebook_psid_login_request";
|
|
||||||
export * from "./fb_id_login_request";
|
|
||||||
export * from "./google_login_request";
|
|
||||||
export * from "./guest_login_request";
|
|
||||||
export * from "./login_request";
|
|
||||||
export * from "./refresh_token_request";
|
|
||||||
export * from "./register_request";
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
/**
|
|
||||||
* 登录请求 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
LoginRequestSchema,
|
|
||||||
type LoginRequestInput,
|
|
||||||
type LoginRequestData,
|
|
||||||
} from "@/data/schemas/auth/request/login_request";
|
|
||||||
|
|
||||||
export class LoginRequest {
|
|
||||||
declare readonly email: string;
|
|
||||||
declare readonly username: string;
|
|
||||||
declare readonly password: string;
|
|
||||||
declare readonly platform: string;
|
|
||||||
declare readonly guestId: string;
|
|
||||||
declare readonly isTestAccount: boolean;
|
|
||||||
|
|
||||||
private constructor(input: LoginRequestInput) {
|
|
||||||
const data = LoginRequestSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: LoginRequestInput): LoginRequest {
|
|
||||||
return new LoginRequest(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): LoginRequest {
|
|
||||||
return LoginRequest.from(json as LoginRequestInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): LoginRequestData {
|
|
||||||
return LoginRequestSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
/**
|
|
||||||
* 刷新 Token 请求 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
RefreshTokenRequestSchema,
|
|
||||||
} from "@/data/schemas/auth/request/refresh_token_request";
|
|
||||||
import {
|
|
||||||
createSchemaDto,
|
|
||||||
type SchemaDto,
|
|
||||||
} from "@/data/dto/schema_dto";
|
|
||||||
|
|
||||||
export type RefreshTokenRequest = SchemaDto<
|
|
||||||
typeof RefreshTokenRequestSchema,
|
|
||||||
"refreshToken"
|
|
||||||
>;
|
|
||||||
export const RefreshTokenRequest = createSchemaDto<
|
|
||||||
typeof RefreshTokenRequestSchema,
|
|
||||||
"refreshToken"
|
|
||||||
>(RefreshTokenRequestSchema);
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
/**
|
|
||||||
* 注册请求 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
RegisterRequestSchema,
|
|
||||||
type RegisterRequestInput,
|
|
||||||
type RegisterRequestData,
|
|
||||||
} from "@/data/schemas/auth/request/register_request";
|
|
||||||
|
|
||||||
export class RegisterRequest {
|
|
||||||
declare readonly username: string;
|
|
||||||
declare readonly email: string;
|
|
||||||
declare readonly password: string;
|
|
||||||
declare readonly platform: string;
|
|
||||||
declare readonly guestId: string;
|
|
||||||
declare readonly isTestAccount: boolean;
|
|
||||||
|
|
||||||
private constructor(input: RegisterRequestInput) {
|
|
||||||
const data = RegisterRequestSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: RegisterRequestInput): RegisterRequest {
|
|
||||||
return new RegisterRequest(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): RegisterRequest {
|
|
||||||
return RegisterRequest.from(json as RegisterRequestInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): RegisterRequestData {
|
|
||||||
return RegisterRequestSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
/**
|
|
||||||
* Facebook identity 绑定响应 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
FacebookIdentityResponseSchema,
|
|
||||||
} from "@/data/schemas/auth/response/facebook_identity_response";
|
|
||||||
import {
|
|
||||||
createSchemaDto,
|
|
||||||
type SchemaDto,
|
|
||||||
} from "@/data/dto/schema_dto";
|
|
||||||
|
|
||||||
export type FacebookIdentityResponse = SchemaDto<
|
|
||||||
typeof FacebookIdentityResponseSchema
|
|
||||||
>;
|
|
||||||
export const FacebookIdentityResponse = createSchemaDto(
|
|
||||||
FacebookIdentityResponseSchema,
|
|
||||||
);
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
/**
|
|
||||||
* Facebook PSID 登录响应 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
FacebookPsidLoginResponseSchema,
|
|
||||||
type FacebookPsidLoginResponseInput,
|
|
||||||
type FacebookPsidLoginResponseData,
|
|
||||||
} from "@/data/schemas/auth/response/facebook_psid_login_response";
|
|
||||||
|
|
||||||
export class FacebookPsidLoginResponse {
|
|
||||||
private constructor(input: FacebookPsidLoginResponseInput) {
|
|
||||||
const data = FacebookPsidLoginResponseSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(
|
|
||||||
input: FacebookPsidLoginResponseInput,
|
|
||||||
): FacebookPsidLoginResponse {
|
|
||||||
return new FacebookPsidLoginResponse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): FacebookPsidLoginResponse {
|
|
||||||
return FacebookPsidLoginResponse.from(
|
|
||||||
json as FacebookPsidLoginResponseInput,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): FacebookPsidLoginResponseData {
|
|
||||||
return FacebookPsidLoginResponseSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
/**
|
|
||||||
* 游客登录响应 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
GuestLoginResponseSchema,
|
|
||||||
type GuestLoginResponseInput,
|
|
||||||
type GuestLoginResponseData,
|
|
||||||
} from "@/data/schemas/auth/response/guest_login_response";
|
|
||||||
import { User } from "@/data/dto/user/user";
|
|
||||||
|
|
||||||
export class GuestLoginResponse {
|
|
||||||
declare readonly token: string;
|
|
||||||
declare readonly userId: string;
|
|
||||||
declare readonly user: User;
|
|
||||||
|
|
||||||
private constructor(input: GuestLoginResponseInput) {
|
|
||||||
const parsed = GuestLoginResponseSchema.parse(input);
|
|
||||||
Object.assign(this, {
|
|
||||||
...parsed,
|
|
||||||
user: parsed.user ? User.fromJson(parsed.user) : User.empty(),
|
|
||||||
});
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: GuestLoginResponseInput): GuestLoginResponse {
|
|
||||||
return new GuestLoginResponse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): GuestLoginResponse {
|
|
||||||
return GuestLoginResponse.from(json as GuestLoginResponseInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): GuestLoginResponseData {
|
|
||||||
const data = GuestLoginResponseSchema.parse(this);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
export * from "./facebook_identity_response";
|
|
||||||
export * from "./facebook_psid_login_response";
|
|
||||||
export * from "./guest_login_response";
|
|
||||||
export * from "./login_response";
|
|
||||||
export * from "./logout_response";
|
|
||||||
export * from "./refresh_token_response";
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
/**
|
|
||||||
* 登录响应 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
LoginResponseSchema,
|
|
||||||
type LoginResponseInput,
|
|
||||||
type LoginResponseData,
|
|
||||||
} from "@/data/schemas/auth/response/login_response";
|
|
||||||
import { User } from "@/data/dto/user/user";
|
|
||||||
|
|
||||||
export class LoginResponse {
|
|
||||||
declare readonly user: User;
|
|
||||||
declare readonly token: string;
|
|
||||||
declare readonly refreshToken: string;
|
|
||||||
|
|
||||||
private constructor(input: LoginResponseInput) {
|
|
||||||
const data = LoginResponseSchema.parse(input);
|
|
||||||
Object.assign(this, {
|
|
||||||
...data,
|
|
||||||
user: User.fromJson(data.user),
|
|
||||||
});
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: LoginResponseInput): LoginResponse {
|
|
||||||
return new LoginResponse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): LoginResponse {
|
|
||||||
return LoginResponse.from(json as LoginResponseInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): LoginResponseData {
|
|
||||||
return LoginResponseSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
/**
|
|
||||||
* 退出登录响应 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
LogoutResponseSchema,
|
|
||||||
} from "@/data/schemas/auth/response/logout_response";
|
|
||||||
import {
|
|
||||||
createSchemaDto,
|
|
||||||
type SchemaDto,
|
|
||||||
} from "@/data/dto/schema_dto";
|
|
||||||
|
|
||||||
export type LogoutResponse = SchemaDto<
|
|
||||||
typeof LogoutResponseSchema,
|
|
||||||
"success"
|
|
||||||
>;
|
|
||||||
export const LogoutResponse = createSchemaDto<
|
|
||||||
typeof LogoutResponseSchema,
|
|
||||||
"success"
|
|
||||||
>(LogoutResponseSchema);
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
/**
|
|
||||||
* 刷新 Token 响应 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
RefreshTokenResponseSchema,
|
|
||||||
type RefreshTokenResponseInput,
|
|
||||||
type RefreshTokenResponseData,
|
|
||||||
} from "@/data/schemas/auth/response/refresh_token_response";
|
|
||||||
|
|
||||||
export class RefreshTokenResponse {
|
|
||||||
declare readonly token: string;
|
|
||||||
declare readonly refreshToken: string;
|
|
||||||
|
|
||||||
private constructor(input: RefreshTokenResponseInput) {
|
|
||||||
const data = RefreshTokenResponseSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: RefreshTokenResponseInput): RefreshTokenResponse {
|
|
||||||
return new RefreshTokenResponse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): RefreshTokenResponse {
|
|
||||||
return RefreshTokenResponse.from(json as RefreshTokenResponseInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): RefreshTokenResponseData {
|
|
||||||
return RefreshTokenResponseSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import {
|
|
||||||
CharacterSchema,
|
|
||||||
type CharacterData,
|
|
||||||
type CharacterInput,
|
|
||||||
} from "@/data/schemas/character";
|
|
||||||
|
|
||||||
export class Character {
|
|
||||||
declare readonly id: string;
|
|
||||||
declare readonly slug: string;
|
|
||||||
declare readonly displayName: string;
|
|
||||||
declare readonly avatarUrl: string;
|
|
||||||
|
|
||||||
private constructor(input: CharacterInput) {
|
|
||||||
Object.assign(this, CharacterSchema.parse(input));
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: CharacterInput): Character {
|
|
||||||
return new Character(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): Character {
|
|
||||||
return Character.from(json as CharacterInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): CharacterData {
|
|
||||||
return CharacterSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import {
|
|
||||||
CharactersResponseSchema,
|
|
||||||
type CharactersResponseData,
|
|
||||||
type CharactersResponseInput,
|
|
||||||
} from "@/data/schemas/character";
|
|
||||||
|
|
||||||
import { Character } from "./character";
|
|
||||||
|
|
||||||
export class CharactersResponse {
|
|
||||||
declare readonly items: Character[];
|
|
||||||
|
|
||||||
private constructor(input: CharactersResponseInput) {
|
|
||||||
const data = CharactersResponseSchema.parse(input);
|
|
||||||
Object.assign(this, {
|
|
||||||
items: data.items.map((item) => Character.from(item)),
|
|
||||||
});
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: CharactersResponseInput): CharactersResponse {
|
|
||||||
return new CharactersResponse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): CharactersResponse {
|
|
||||||
return CharactersResponse.from(json as CharactersResponseInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): CharactersResponseData {
|
|
||||||
return CharactersResponseSchema.parse({
|
|
||||||
items: this.items.map((item) => item.toJson()),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export * from "./character";
|
|
||||||
export * from "./characters_response";
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
/**
|
|
||||||
* 聊天列表项 discriminated union
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* 4 种类型:
|
|
||||||
* - ai-disclosure:顶部 AI 披露横幅
|
|
||||||
* - date-separator:日期分隔条
|
|
||||||
* - loading-animation:AI 正在输入的动画
|
|
||||||
* - message:实际消息
|
|
||||||
*/
|
|
||||||
import type { UiMessage } from "./ui_message";
|
|
||||||
|
|
||||||
export type ChatListItem =
|
|
||||||
| { type: "ai-disclosure" }
|
|
||||||
| { type: "date-separator"; date: string }
|
|
||||||
| { type: "loading-animation" }
|
|
||||||
| { type: "message"; message: UiMessage; key: string };
|
|
||||||
|
|
||||||
export const ChatListItemType = {
|
|
||||||
AiDisclosure: "ai-disclosure",
|
|
||||||
DateSeparator: "date-separator",
|
|
||||||
LoadingAnimation: "loading-animation",
|
|
||||||
Message: "message",
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export type ChatListItemType =
|
|
||||||
(typeof ChatListItemType)[keyof typeof ChatListItemType];
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
/**
|
|
||||||
* 聊天历史中的单条消息 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
ChatMessageSchema,
|
|
||||||
type ChatMessageInput,
|
|
||||||
type ChatMessageData,
|
|
||||||
type ChatImageData,
|
|
||||||
type ChatLockDetailData,
|
|
||||||
} from "@/data/schemas/chat";
|
|
||||||
|
|
||||||
export class ChatMessage {
|
|
||||||
declare readonly role: string;
|
|
||||||
declare readonly type: string;
|
|
||||||
declare readonly content: string;
|
|
||||||
declare readonly id: string;
|
|
||||||
declare readonly createdAt: string;
|
|
||||||
declare readonly audioUrl: string | null;
|
|
||||||
declare readonly image: ChatImageData;
|
|
||||||
declare readonly lockDetail: ChatLockDetailData;
|
|
||||||
|
|
||||||
private constructor(input: ChatMessageInput) {
|
|
||||||
const data = ChatMessageSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: ChatMessageInput): ChatMessage {
|
|
||||||
return new ChatMessage(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): ChatMessage {
|
|
||||||
return ChatMessage.from(json as ChatMessageInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): ChatMessageData {
|
|
||||||
return ChatMessageSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file Automatically generated by barrelsby.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export * from "./chat_list_item";
|
|
||||||
export * from "./chat_media";
|
|
||||||
export * from "./chat_message";
|
|
||||||
export * from "./request";
|
|
||||||
export * from "./response";
|
|
||||||
export * from "./ui_message";
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export * from "./send_message_request";
|
|
||||||
export * from "./unlock_private_request";
|
|
||||||
export * from "./unlock_history_request";
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
/**
|
|
||||||
* 发送消息请求 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
SendMessageRequestSchema,
|
|
||||||
type SendMessageRequestInput,
|
|
||||||
type SendMessageRequestData,
|
|
||||||
} from "@/data/schemas/chat/request/send_message_request";
|
|
||||||
|
|
||||||
export class SendMessageRequest {
|
|
||||||
declare readonly characterId: string;
|
|
||||||
declare readonly message: string;
|
|
||||||
declare readonly image: string;
|
|
||||||
declare readonly imageId: string;
|
|
||||||
declare readonly imageThumbUrl: string;
|
|
||||||
declare readonly imageMediumUrl: string;
|
|
||||||
declare readonly imageOriginalUrl: string;
|
|
||||||
declare readonly imageWidth: number;
|
|
||||||
declare readonly imageHeight: number;
|
|
||||||
|
|
||||||
private constructor(input: SendMessageRequestInput) {
|
|
||||||
const data = SendMessageRequestSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: SendMessageRequestInput): SendMessageRequest {
|
|
||||||
return new SendMessageRequest(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): SendMessageRequest {
|
|
||||||
return SendMessageRequest.from(json as SendMessageRequestInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): SendMessageRequestData {
|
|
||||||
return SendMessageRequestSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import {
|
|
||||||
UnlockHistoryRequestSchema,
|
|
||||||
type UnlockHistoryRequestData,
|
|
||||||
type UnlockHistoryRequestInput,
|
|
||||||
} from "@/data/schemas/chat/request/unlock_history_request";
|
|
||||||
|
|
||||||
export class UnlockHistoryRequest {
|
|
||||||
declare readonly characterId: string;
|
|
||||||
|
|
||||||
private constructor(input: UnlockHistoryRequestInput) {
|
|
||||||
Object.assign(this, UnlockHistoryRequestSchema.parse(input));
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: UnlockHistoryRequestInput): UnlockHistoryRequest {
|
|
||||||
return new UnlockHistoryRequest(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): UnlockHistoryRequestData {
|
|
||||||
return UnlockHistoryRequestSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/**
|
|
||||||
* 私密消息解锁请求 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
UnlockPrivateRequestSchema,
|
|
||||||
type UnlockPrivateRequestData,
|
|
||||||
type UnlockPrivateRequestInput,
|
|
||||||
} from "@/data/schemas/chat/request/unlock_private_request";
|
|
||||||
|
|
||||||
export class UnlockPrivateRequest {
|
|
||||||
declare readonly characterId: string;
|
|
||||||
declare readonly messageId?: string;
|
|
||||||
declare readonly lockType?: UnlockPrivateRequestData["lockType"];
|
|
||||||
declare readonly clientLockId?: string;
|
|
||||||
|
|
||||||
private constructor(input: UnlockPrivateRequestInput) {
|
|
||||||
const data = UnlockPrivateRequestSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: UnlockPrivateRequestInput): UnlockPrivateRequest {
|
|
||||||
return new UnlockPrivateRequest(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): UnlockPrivateRequest {
|
|
||||||
return UnlockPrivateRequest.from(json as UnlockPrivateRequestInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): UnlockPrivateRequestData {
|
|
||||||
return UnlockPrivateRequestSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
/**
|
|
||||||
* 聊天历史响应 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
ChatHistoryResponseSchema,
|
|
||||||
type ChatHistoryResponseInput,
|
|
||||||
type ChatHistoryResponseData,
|
|
||||||
} from "@/data/schemas/chat/response/chat_history_response";
|
|
||||||
import { ChatMessage } from "../chat_message";
|
|
||||||
|
|
||||||
export class ChatHistoryResponse {
|
|
||||||
declare readonly messages: ChatMessage[];
|
|
||||||
declare readonly total: number;
|
|
||||||
declare readonly limit: number;
|
|
||||||
|
|
||||||
private constructor(input: ChatHistoryResponseInput) {
|
|
||||||
const data = ChatHistoryResponseSchema.parse(input);
|
|
||||||
Object.assign(this, {
|
|
||||||
...data,
|
|
||||||
messages: data.messages.map((m) => ChatMessage.from(m)),
|
|
||||||
});
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: ChatHistoryResponseInput): ChatHistoryResponse {
|
|
||||||
return new ChatHistoryResponse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): ChatHistoryResponse {
|
|
||||||
return ChatHistoryResponse.from(json as ChatHistoryResponseInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): ChatHistoryResponseData {
|
|
||||||
return ChatHistoryResponseSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
/**
|
|
||||||
* 发送消息响应 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
ChatSendResponseSchema,
|
|
||||||
type ChatSendResponseInput,
|
|
||||||
type ChatSendResponseData,
|
|
||||||
type ChatImageData,
|
|
||||||
type ChatLockDetailData,
|
|
||||||
} from "@/data/schemas/chat";
|
|
||||||
|
|
||||||
export class ChatSendResponse {
|
|
||||||
declare readonly reply: string;
|
|
||||||
declare readonly audioUrl: string;
|
|
||||||
declare readonly messageId: string;
|
|
||||||
declare readonly timestamp: number;
|
|
||||||
declare readonly image: ChatImageData;
|
|
||||||
declare readonly lockDetail: ChatLockDetailData;
|
|
||||||
declare readonly canSendMessage: boolean;
|
|
||||||
declare readonly creditBalance: number;
|
|
||||||
declare readonly creditsCharged: number;
|
|
||||||
declare readonly requiredCredits: number;
|
|
||||||
declare readonly shortfallCredits: number;
|
|
||||||
|
|
||||||
private constructor(input: ChatSendResponseInput) {
|
|
||||||
const data = ChatSendResponseSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: ChatSendResponseInput): ChatSendResponse {
|
|
||||||
return new ChatSendResponse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): ChatSendResponse {
|
|
||||||
return ChatSendResponse.from(json as ChatSendResponseInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): ChatSendResponseData {
|
|
||||||
return ChatSendResponseSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export * from "./chat_history_response";
|
|
||||||
export * from "./chat_send_response";
|
|
||||||
export * from "./unlock_history_response";
|
|
||||||
export * from "./unlock_private_response";
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/**
|
|
||||||
* 一键解锁历史锁定消息响应 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
UnlockHistoryResponseSchema,
|
|
||||||
type UnlockHistoryReason,
|
|
||||||
type UnlockHistoryResponseData,
|
|
||||||
type UnlockHistoryResponseInput,
|
|
||||||
} from "@/data/schemas/chat/response/unlock_history_response";
|
|
||||||
|
|
||||||
export class UnlockHistoryResponse {
|
|
||||||
declare readonly unlocked: boolean;
|
|
||||||
declare readonly reason: UnlockHistoryReason;
|
|
||||||
declare readonly shortfallCredits: number;
|
|
||||||
|
|
||||||
private constructor(input: UnlockHistoryResponseInput) {
|
|
||||||
const data = UnlockHistoryResponseSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: UnlockHistoryResponseInput): UnlockHistoryResponse {
|
|
||||||
return new UnlockHistoryResponse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): UnlockHistoryResponse {
|
|
||||||
return UnlockHistoryResponse.from(json as UnlockHistoryResponseInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): UnlockHistoryResponseData {
|
|
||||||
return UnlockHistoryResponseSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
import {
|
|
||||||
UnlockPrivateResponseSchema,
|
|
||||||
type UnlockPrivateReason,
|
|
||||||
type UnlockPrivateResponseData,
|
|
||||||
type UnlockPrivateResponseInput,
|
|
||||||
} from "@/data/schemas/chat/response/unlock_private_response";
|
|
||||||
import type { ChatImageData } from "@/data/schemas/chat";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 单条历史付费 / 私密消息解锁响应 DTO。
|
|
||||||
*/
|
|
||||||
export class UnlockPrivateResponse {
|
|
||||||
declare readonly unlocked: boolean;
|
|
||||||
declare readonly messageId: string;
|
|
||||||
declare readonly content: string;
|
|
||||||
declare readonly audioUrl: string;
|
|
||||||
declare readonly image: ChatImageData;
|
|
||||||
declare readonly reason: UnlockPrivateReason;
|
|
||||||
declare readonly creditBalance: number;
|
|
||||||
declare readonly creditsCharged: number;
|
|
||||||
declare readonly requiredCredits: number;
|
|
||||||
declare readonly shortfallCredits: number;
|
|
||||||
|
|
||||||
private constructor(input: UnlockPrivateResponseInput) {
|
|
||||||
const data = UnlockPrivateResponseSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: UnlockPrivateResponseInput): UnlockPrivateResponse {
|
|
||||||
return new UnlockPrivateResponse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): UnlockPrivateResponse {
|
|
||||||
return UnlockPrivateResponse.from(json as UnlockPrivateResponseInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): UnlockPrivateResponseData {
|
|
||||||
return UnlockPrivateResponseSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export * from "./feedback_submission";
|
|
||||||
export * from "./response";
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import {
|
|
||||||
FeedbackSubmitResponseSchema,
|
|
||||||
} from "@/data/schemas/feedback";
|
|
||||||
import {
|
|
||||||
createSchemaDto,
|
|
||||||
type SchemaDto,
|
|
||||||
} from "@/data/dto/schema_dto";
|
|
||||||
|
|
||||||
export type FeedbackSubmitResponse = SchemaDto<
|
|
||||||
typeof FeedbackSubmitResponseSchema,
|
|
||||||
"feedbackId"
|
|
||||||
>;
|
|
||||||
export const FeedbackSubmitResponse = createSchemaDto<
|
|
||||||
typeof FeedbackSubmitResponseSchema,
|
|
||||||
"feedbackId"
|
|
||||||
>(FeedbackSubmitResponseSchema);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from "./feedback_submit_response";
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file Automatically generated by barrelsby.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export * from "./request";
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
/**
|
|
||||||
* 应用事件上报请求 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
AppEventSchema,
|
|
||||||
} from "@/data/schemas/metrics/request/app_event";
|
|
||||||
import {
|
|
||||||
createSchemaDto,
|
|
||||||
type SchemaDto,
|
|
||||||
} from "@/data/dto/schema_dto";
|
|
||||||
|
|
||||||
type AppEventPublicKey = "userId" | "browser" | "userAgent";
|
|
||||||
export type AppEvent = SchemaDto<typeof AppEventSchema, AppEventPublicKey>;
|
|
||||||
export const AppEvent = createSchemaDto<
|
|
||||||
typeof AppEventSchema,
|
|
||||||
AppEventPublicKey
|
|
||||||
>(AppEventSchema);
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export * from "./app_event";
|
|
||||||
export * from "./pwa_event";
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
/**
|
|
||||||
* PWA 事件上报请求 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
PwaEventSchema,
|
|
||||||
} from "@/data/schemas/metrics/request/pwa_event";
|
|
||||||
import {
|
|
||||||
createSchemaDto,
|
|
||||||
type SchemaDto,
|
|
||||||
} from "@/data/dto/schema_dto";
|
|
||||||
|
|
||||||
type PwaEventPublicKey =
|
|
||||||
| "deviceId"
|
|
||||||
| "deviceType"
|
|
||||||
| "timestamp"
|
|
||||||
| "pwaInstalled"
|
|
||||||
| "pwaSupported";
|
|
||||||
export type PwaEvent = SchemaDto<typeof PwaEventSchema, PwaEventPublicKey>;
|
|
||||||
export const PwaEvent = createSchemaDto<
|
|
||||||
typeof PwaEventSchema,
|
|
||||||
PwaEventPublicKey
|
|
||||||
>(PwaEventSchema);
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file Payment DTO barrel.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export * from "./payment_plan";
|
|
||||||
export * from "./tip_payment_plan";
|
|
||||||
export * from "./request";
|
|
||||||
export * from "./response";
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
/**
|
|
||||||
* 支付套餐 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
PaymentPlanSchema,
|
|
||||||
type PaymentPlanData,
|
|
||||||
type PaymentPlanInput,
|
|
||||||
} from "@/data/schemas/payment/payment_plan";
|
|
||||||
|
|
||||||
export class PaymentPlan {
|
|
||||||
declare readonly planId: string;
|
|
||||||
declare readonly planName: string;
|
|
||||||
declare readonly orderType: string;
|
|
||||||
declare readonly vipDays: number | null;
|
|
||||||
declare readonly dolAmount: number | null;
|
|
||||||
declare readonly creditBalance: number;
|
|
||||||
declare readonly amountCents: number;
|
|
||||||
declare readonly originalAmountCents: number | null;
|
|
||||||
declare readonly currency: string;
|
|
||||||
declare readonly isFirstRechargeOffer: boolean;
|
|
||||||
declare readonly mostPopular: boolean;
|
|
||||||
declare readonly firstRechargeDiscountPercent: number | null;
|
|
||||||
declare readonly promotionType: string | null;
|
|
||||||
|
|
||||||
private constructor(input: PaymentPlanInput) {
|
|
||||||
const data = PaymentPlanSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: PaymentPlanInput): PaymentPlan {
|
|
||||||
return new PaymentPlan(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): PaymentPlan {
|
|
||||||
return PaymentPlan.from(json as PaymentPlanInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): PaymentPlanData {
|
|
||||||
return PaymentPlanSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
/**
|
|
||||||
* 创建支付订单请求 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
CreatePaymentOrderRequestSchema,
|
|
||||||
type CreatePaymentOrderRequestData,
|
|
||||||
type CreatePaymentOrderRequestInput,
|
|
||||||
type PayChannel,
|
|
||||||
} from "@/data/schemas/payment/request/create_payment_order_request";
|
|
||||||
|
|
||||||
export type { PayChannel };
|
|
||||||
|
|
||||||
export class CreatePaymentOrderRequest {
|
|
||||||
declare readonly planId: string;
|
|
||||||
declare readonly payChannel: PayChannel;
|
|
||||||
declare readonly autoRenew: boolean;
|
|
||||||
|
|
||||||
private constructor(input: CreatePaymentOrderRequestInput) {
|
|
||||||
const data = CreatePaymentOrderRequestSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(
|
|
||||||
input: CreatePaymentOrderRequestInput,
|
|
||||||
): CreatePaymentOrderRequest {
|
|
||||||
return new CreatePaymentOrderRequest(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): CreatePaymentOrderRequest {
|
|
||||||
return CreatePaymentOrderRequest.from(
|
|
||||||
json as CreatePaymentOrderRequestInput,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): CreatePaymentOrderRequestData {
|
|
||||||
return CreatePaymentOrderRequestSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from "./create_payment_order_request";
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
/**
|
|
||||||
* 创建支付订单响应 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
CreatePaymentOrderResponseSchema,
|
|
||||||
type CreatePaymentOrderResponseData,
|
|
||||||
type CreatePaymentOrderResponseInput,
|
|
||||||
} from "@/data/schemas/payment/response/create_payment_order_response";
|
|
||||||
|
|
||||||
export class CreatePaymentOrderResponse {
|
|
||||||
declare readonly orderId: string;
|
|
||||||
declare readonly payParams: Record<string, unknown>;
|
|
||||||
|
|
||||||
private constructor(input: CreatePaymentOrderResponseInput) {
|
|
||||||
const data = CreatePaymentOrderResponseSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(
|
|
||||||
input: CreatePaymentOrderResponseInput,
|
|
||||||
): CreatePaymentOrderResponse {
|
|
||||||
return new CreatePaymentOrderResponse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): CreatePaymentOrderResponse {
|
|
||||||
return CreatePaymentOrderResponse.from(
|
|
||||||
json as CreatePaymentOrderResponseInput,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): CreatePaymentOrderResponseData {
|
|
||||||
return CreatePaymentOrderResponseSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export * from "./create_payment_order_response";
|
|
||||||
export * from "./payment_order_status_response";
|
|
||||||
export * from "./payment_plans_response";
|
|
||||||
export * from "./tip_payment_plans_response";
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
/**
|
|
||||||
* 支付订单状态响应 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
PaymentOrderStatusResponseSchema,
|
|
||||||
type PaymentOrderStatus,
|
|
||||||
type PaymentOrderStatusResponseData,
|
|
||||||
type PaymentOrderStatusResponseInput,
|
|
||||||
} from "@/data/schemas/payment/response/payment_order_status_response";
|
|
||||||
|
|
||||||
export type { PaymentOrderStatus };
|
|
||||||
|
|
||||||
export class PaymentOrderStatusResponse {
|
|
||||||
declare readonly status: PaymentOrderStatus;
|
|
||||||
|
|
||||||
private constructor(input: PaymentOrderStatusResponseInput) {
|
|
||||||
const data = PaymentOrderStatusResponseSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(
|
|
||||||
input: PaymentOrderStatusResponseInput,
|
|
||||||
): PaymentOrderStatusResponse {
|
|
||||||
return new PaymentOrderStatusResponse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): PaymentOrderStatusResponse {
|
|
||||||
return PaymentOrderStatusResponse.from(
|
|
||||||
json as PaymentOrderStatusResponseInput,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): PaymentOrderStatusResponseData {
|
|
||||||
return PaymentOrderStatusResponseSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
/**
|
|
||||||
* 支付套餐列表响应 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
PaymentPlansResponseSchema,
|
|
||||||
type FirstRechargeOfferData,
|
|
||||||
type PaymentPlansResponseData,
|
|
||||||
type PaymentPlansResponseInput,
|
|
||||||
} from "@/data/schemas/payment/response/payment_plans_response";
|
|
||||||
|
|
||||||
import { PaymentPlan } from "../payment_plan";
|
|
||||||
|
|
||||||
export class PaymentPlansResponse {
|
|
||||||
declare readonly isFirstRecharge: boolean;
|
|
||||||
declare readonly firstRechargeOffer: FirstRechargeOfferData | null;
|
|
||||||
declare readonly plans: PaymentPlan[];
|
|
||||||
|
|
||||||
private constructor(input: PaymentPlansResponseInput) {
|
|
||||||
const data = PaymentPlansResponseSchema.parse(input);
|
|
||||||
Object.assign(this, {
|
|
||||||
isFirstRecharge: data.isFirstRecharge,
|
|
||||||
firstRechargeOffer: data.firstRechargeOffer,
|
|
||||||
plans: data.plans.map((plan) => PaymentPlan.from(plan)),
|
|
||||||
});
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: PaymentPlansResponseInput): PaymentPlansResponse {
|
|
||||||
return new PaymentPlansResponse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): PaymentPlansResponse {
|
|
||||||
return PaymentPlansResponse.from(json as PaymentPlansResponseInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): PaymentPlansResponseData {
|
|
||||||
return {
|
|
||||||
isFirstRecharge: this.isFirstRecharge,
|
|
||||||
firstRechargeOffer: this.firstRechargeOffer,
|
|
||||||
plans: this.plans.map((plan) => plan.toJson()),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type FirstRechargeOffer = FirstRechargeOfferData;
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import {
|
|
||||||
TipPaymentPlansResponseSchema,
|
|
||||||
type TipPaymentPlansResponseData,
|
|
||||||
type TipPaymentPlansResponseInput,
|
|
||||||
} from "@/data/schemas/payment/response/tip_payment_plans_response";
|
|
||||||
|
|
||||||
import { TipPaymentPlan } from "../tip_payment_plan";
|
|
||||||
|
|
||||||
export class TipPaymentPlansResponse {
|
|
||||||
declare readonly plans: TipPaymentPlan[];
|
|
||||||
|
|
||||||
private constructor(input: TipPaymentPlansResponseInput) {
|
|
||||||
const data = TipPaymentPlansResponseSchema.parse(input);
|
|
||||||
Object.assign(this, {
|
|
||||||
plans: data.plans.map((plan) => TipPaymentPlan.from(plan)),
|
|
||||||
});
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: TipPaymentPlansResponseInput): TipPaymentPlansResponse {
|
|
||||||
return new TipPaymentPlansResponse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): TipPaymentPlansResponse {
|
|
||||||
return TipPaymentPlansResponse.from(
|
|
||||||
json as TipPaymentPlansResponseInput,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): TipPaymentPlansResponseData {
|
|
||||||
return {
|
|
||||||
plans: this.plans.map((plan) => plan.toJson()),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import {
|
|
||||||
TipPaymentPlanSchema,
|
|
||||||
type TipPaymentPlanData,
|
|
||||||
type TipPaymentPlanInput,
|
|
||||||
} from "@/data/schemas/payment/tip_payment_plan";
|
|
||||||
|
|
||||||
export class TipPaymentPlan {
|
|
||||||
declare readonly planId: string;
|
|
||||||
declare readonly planName: string;
|
|
||||||
declare readonly amountCents: number;
|
|
||||||
declare readonly currency: string;
|
|
||||||
|
|
||||||
private constructor(input: TipPaymentPlanInput) {
|
|
||||||
const data = TipPaymentPlanSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: TipPaymentPlanInput): TipPaymentPlan {
|
|
||||||
return new TipPaymentPlan(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): TipPaymentPlan {
|
|
||||||
return TipPaymentPlan.from(json as TipPaymentPlanInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): TipPaymentPlanData {
|
|
||||||
return TipPaymentPlanSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export * from "./private_album";
|
|
||||||
export * from "./request";
|
|
||||||
export * from "./response";
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import {
|
|
||||||
PrivateAlbumSchema,
|
|
||||||
type PrivateAlbumData,
|
|
||||||
type PrivateAlbumInput,
|
|
||||||
} from "@/data/schemas/private-room";
|
|
||||||
|
|
||||||
export class PrivateAlbum {
|
|
||||||
declare readonly albumId: string;
|
|
||||||
declare readonly title: string;
|
|
||||||
declare readonly content: string | null;
|
|
||||||
declare readonly previewText: string;
|
|
||||||
declare readonly imageCount: number;
|
|
||||||
declare readonly images: PrivateAlbumData["images"];
|
|
||||||
declare readonly locked: boolean;
|
|
||||||
declare readonly unlocked: boolean;
|
|
||||||
declare readonly unlockCost: number;
|
|
||||||
declare readonly publishedAt: string | null;
|
|
||||||
declare readonly lockDetail: PrivateAlbumData["lockDetail"];
|
|
||||||
|
|
||||||
private constructor(input: PrivateAlbumInput) {
|
|
||||||
const data = PrivateAlbumSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: PrivateAlbumInput): PrivateAlbum {
|
|
||||||
return new PrivateAlbum(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): PrivateAlbum {
|
|
||||||
return PrivateAlbum.from(json as PrivateAlbumInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): PrivateAlbumData {
|
|
||||||
return PrivateAlbumSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from "./unlock_private_album_request";
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import {
|
|
||||||
UnlockPrivateAlbumRequestSchema,
|
|
||||||
type UnlockPrivateAlbumRequestData,
|
|
||||||
type UnlockPrivateAlbumRequestInput,
|
|
||||||
} from "@/data/schemas/private-room";
|
|
||||||
|
|
||||||
export class UnlockPrivateAlbumRequest {
|
|
||||||
private constructor(input: UnlockPrivateAlbumRequestInput) {
|
|
||||||
const data = UnlockPrivateAlbumRequestSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: UnlockPrivateAlbumRequestInput): UnlockPrivateAlbumRequest {
|
|
||||||
return new UnlockPrivateAlbumRequest(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): UnlockPrivateAlbumRequestData {
|
|
||||||
return UnlockPrivateAlbumRequestSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export * from "./private_albums_response";
|
|
||||||
export * from "./private_album_unlock_response";
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
import {
|
|
||||||
PrivateAlbumUnlockResponseSchema,
|
|
||||||
type PrivateAlbumUnlockReason,
|
|
||||||
type PrivateAlbumUnlockResponseData,
|
|
||||||
type PrivateAlbumUnlockResponseInput,
|
|
||||||
} from "@/data/schemas/private-room";
|
|
||||||
|
|
||||||
export class PrivateAlbumUnlockResponse {
|
|
||||||
declare readonly albumId: string;
|
|
||||||
declare readonly locked: boolean;
|
|
||||||
declare readonly unlocked: boolean;
|
|
||||||
declare readonly reason: PrivateAlbumUnlockReason | string;
|
|
||||||
declare readonly unlockCost: number;
|
|
||||||
declare readonly requiredCredits: number;
|
|
||||||
declare readonly creditBalance: number;
|
|
||||||
declare readonly shortfallCredits: number;
|
|
||||||
declare readonly images: PrivateAlbumUnlockResponseData["images"];
|
|
||||||
|
|
||||||
private constructor(input: PrivateAlbumUnlockResponseInput) {
|
|
||||||
const data = PrivateAlbumUnlockResponseSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: PrivateAlbumUnlockResponseInput): PrivateAlbumUnlockResponse {
|
|
||||||
return new PrivateAlbumUnlockResponse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): PrivateAlbumUnlockResponse {
|
|
||||||
return PrivateAlbumUnlockResponse.from(
|
|
||||||
json as PrivateAlbumUnlockResponseInput,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): PrivateAlbumUnlockResponseData {
|
|
||||||
return PrivateAlbumUnlockResponseSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import {
|
|
||||||
PrivateAlbumsResponseSchema,
|
|
||||||
type PrivateAlbumsResponseData,
|
|
||||||
type PrivateAlbumsResponseInput,
|
|
||||||
} from "@/data/schemas/private-room";
|
|
||||||
|
|
||||||
import { PrivateAlbum } from "../private_album";
|
|
||||||
|
|
||||||
export class PrivateAlbumsResponse {
|
|
||||||
declare readonly items: PrivateAlbum[];
|
|
||||||
declare readonly creditBalance: number;
|
|
||||||
|
|
||||||
private constructor(input: PrivateAlbumsResponseInput) {
|
|
||||||
const data = PrivateAlbumsResponseSchema.parse(input);
|
|
||||||
Object.assign(this, {
|
|
||||||
items: data.items.map((item) => PrivateAlbum.from(item)),
|
|
||||||
creditBalance: data.creditBalance,
|
|
||||||
});
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: PrivateAlbumsResponseInput): PrivateAlbumsResponse {
|
|
||||||
return new PrivateAlbumsResponse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): PrivateAlbumsResponse {
|
|
||||||
return PrivateAlbumsResponse.from(json as PrivateAlbumsResponseInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): PrivateAlbumsResponseData {
|
|
||||||
return {
|
|
||||||
items: this.items.map((item) => item.toJson()),
|
|
||||||
creditBalance: this.creditBalance,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
import type { z } from "zod";
|
|
||||||
|
|
||||||
export type SchemaDto<
|
|
||||||
TSchema extends z.ZodType,
|
|
||||||
TPublicKey extends keyof z.output<TSchema> = never,
|
|
||||||
> = Readonly<Pick<z.output<TSchema>, TPublicKey>> & {
|
|
||||||
toJson(): z.output<TSchema>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface SchemaDtoFactory<
|
|
||||||
TSchema extends z.ZodType,
|
|
||||||
TPublicKey extends keyof z.output<TSchema> = never,
|
|
||||||
> {
|
|
||||||
readonly prototype: SchemaDto<TSchema, TPublicKey>;
|
|
||||||
[Symbol.hasInstance](value: unknown): boolean;
|
|
||||||
from(input: z.input<TSchema>): SchemaDto<TSchema, TPublicKey>;
|
|
||||||
fromJson(json: unknown): SchemaDto<TSchema, TPublicKey>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 为没有自定义映射逻辑的对象 Schema 创建不可变 DTO。
|
|
||||||
*
|
|
||||||
* `TPublicKey` 只暴露业务代码实际读取的字段;完整解析结果仍会保留在实例中,
|
|
||||||
* 并由 `toJson` 按原 Schema 输出。包含嵌套 DTO 转换或领域方法的模型继续使用
|
|
||||||
* 显式 class,避免把业务逻辑隐藏进通用工厂。
|
|
||||||
*/
|
|
||||||
export function createSchemaDto<
|
|
||||||
TSchema extends z.ZodType,
|
|
||||||
TPublicKey extends keyof z.output<TSchema> = never,
|
|
||||||
>(schema: TSchema): SchemaDtoFactory<TSchema, TPublicKey> {
|
|
||||||
class GeneratedSchemaDto {
|
|
||||||
private constructor(input: z.input<TSchema>) {
|
|
||||||
Object.assign(this, schema.parse(input) as object);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(
|
|
||||||
input: z.input<TSchema>,
|
|
||||||
): SchemaDto<TSchema, TPublicKey> {
|
|
||||||
return new GeneratedSchemaDto(input) as SchemaDto<
|
|
||||||
TSchema,
|
|
||||||
TPublicKey
|
|
||||||
>;
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): SchemaDto<TSchema, TPublicKey> {
|
|
||||||
return GeneratedSchemaDto.from(json as z.input<TSchema>);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): z.output<TSchema> {
|
|
||||||
return schema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return GeneratedSchemaDto as unknown as SchemaDtoFactory<
|
|
||||||
TSchema,
|
|
||||||
TPublicKey
|
|
||||||
>;
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
/**
|
|
||||||
* 头像数据 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
AvatarDataSchema,
|
|
||||||
type AvatarDataInput,
|
|
||||||
type AvatarDataData,
|
|
||||||
} from "@/data/schemas/user/avatar_data";
|
|
||||||
|
|
||||||
export class AvatarData {
|
|
||||||
declare readonly avatarUrl: string;
|
|
||||||
declare readonly userId: string;
|
|
||||||
|
|
||||||
private constructor(input: AvatarDataInput) {
|
|
||||||
const data = AvatarDataSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: AvatarDataInput): AvatarData {
|
|
||||||
return new AvatarData(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): AvatarData {
|
|
||||||
return AvatarData.from(json as AvatarDataInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): AvatarDataData {
|
|
||||||
return AvatarDataSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file Automatically generated by barrelsby.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export * from "./avatar_data";
|
|
||||||
export * from "./personality_traits";
|
|
||||||
export * from "./recent_memory";
|
|
||||||
export * from "./user";
|
|
||||||
export * from "./user_entitlements";
|
|
||||||
export * from "./user_view";
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
/**
|
|
||||||
* 个性特征 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
PersonalityTraitsSchema,
|
|
||||||
type PersonalityTraitsInput,
|
|
||||||
type PersonalityTraitsData,
|
|
||||||
} from "@/data/schemas/user/personality_traits";
|
|
||||||
|
|
||||||
export class PersonalityTraits {
|
|
||||||
declare readonly cheerful: number;
|
|
||||||
declare readonly caring: number;
|
|
||||||
declare readonly playful: number;
|
|
||||||
declare readonly serious: number;
|
|
||||||
declare readonly romantic: number;
|
|
||||||
|
|
||||||
private constructor(input: PersonalityTraitsInput) {
|
|
||||||
const data = PersonalityTraitsSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: PersonalityTraitsInput): PersonalityTraits {
|
|
||||||
return new PersonalityTraits(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): PersonalityTraits {
|
|
||||||
return PersonalityTraits.from(json as PersonalityTraitsInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): PersonalityTraitsData {
|
|
||||||
return PersonalityTraitsSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/**
|
|
||||||
* 最近记忆 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
RecentMemorySchema,
|
|
||||||
type RecentMemoryInput,
|
|
||||||
type RecentMemoryData,
|
|
||||||
} from "@/data/schemas/user/recent_memory";
|
|
||||||
|
|
||||||
export class RecentMemory {
|
|
||||||
declare readonly type: string;
|
|
||||||
declare readonly content: string;
|
|
||||||
declare readonly importance: number;
|
|
||||||
declare readonly createdAt: string;
|
|
||||||
|
|
||||||
private constructor(input: RecentMemoryInput) {
|
|
||||||
const data = RecentMemorySchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: RecentMemoryInput): RecentMemory {
|
|
||||||
return new RecentMemory(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): RecentMemory {
|
|
||||||
return RecentMemory.from(json as RecentMemoryInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): RecentMemoryData {
|
|
||||||
return RecentMemorySchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
/**
|
|
||||||
* 用户 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
UserSchema,
|
|
||||||
type UserInput,
|
|
||||||
type UserData,
|
|
||||||
} from "@/data/schemas/user/user";
|
|
||||||
import { PersonalityTraits } from "./personality_traits";
|
|
||||||
|
|
||||||
export class User {
|
|
||||||
declare readonly id: string;
|
|
||||||
declare readonly username: string;
|
|
||||||
declare readonly email: string;
|
|
||||||
declare readonly platform: string;
|
|
||||||
declare readonly country: string;
|
|
||||||
declare readonly countryCode: string;
|
|
||||||
declare readonly fbAsid: string;
|
|
||||||
declare readonly fbPsid: string;
|
|
||||||
declare readonly intimacy: number;
|
|
||||||
declare readonly dolBalance: number;
|
|
||||||
declare readonly creditBalance: number;
|
|
||||||
declare readonly dailyFreeChatLimit: number;
|
|
||||||
declare readonly dailyFreeChatUsed: number;
|
|
||||||
declare readonly dailyFreeChatRemaining: number;
|
|
||||||
declare readonly dailyFreePrivateLimit: number;
|
|
||||||
declare readonly dailyFreePrivateUsed: number;
|
|
||||||
declare readonly dailyFreePrivateRemaining: number;
|
|
||||||
declare readonly personalityTraits: PersonalityTraits;
|
|
||||||
declare readonly preferredLanguage: string;
|
|
||||||
declare readonly createdAt: string;
|
|
||||||
declare readonly lastMessageAt: string;
|
|
||||||
|
|
||||||
private constructor(input: UserInput) {
|
|
||||||
const data = UserSchema.parse(input);
|
|
||||||
Object.assign(this, {
|
|
||||||
...data,
|
|
||||||
personalityTraits: PersonalityTraits.from(data.personalityTraits),
|
|
||||||
});
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: UserInput): User {
|
|
||||||
return new User(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): User {
|
|
||||||
return User.from(json as UserInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建一个具有空 id/username 的 User 实例(用于可选 user 字段的默认占位)
|
|
||||||
*/
|
|
||||||
static empty(): User {
|
|
||||||
return new User({ id: "", username: "" });
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): UserData {
|
|
||||||
return UserSchema.parse({
|
|
||||||
...this,
|
|
||||||
personalityTraits: this.personalityTraits.toJson(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
/**
|
|
||||||
* 用户权益快照 DTO
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
UserEntitlementsSchema,
|
|
||||||
type UserEntitlementsData,
|
|
||||||
type UserEntitlementsInput,
|
|
||||||
} from "@/data/schemas/user/user_entitlements";
|
|
||||||
|
|
||||||
export class UserEntitlements {
|
|
||||||
declare readonly userId: string;
|
|
||||||
declare readonly isGuest: boolean;
|
|
||||||
declare readonly isVip: boolean;
|
|
||||||
declare readonly vipExpiresAt: string | null;
|
|
||||||
declare readonly creditBalance: number;
|
|
||||||
declare readonly dolBalance: number;
|
|
||||||
declare readonly policy: UserEntitlementsData["policy"];
|
|
||||||
declare readonly costs: UserEntitlementsData["costs"];
|
|
||||||
declare readonly quotas: UserEntitlementsData["quotas"];
|
|
||||||
declare readonly historyUnlock: UserEntitlementsData["historyUnlock"];
|
|
||||||
|
|
||||||
private constructor(input: UserEntitlementsInput) {
|
|
||||||
const data = UserEntitlementsSchema.parse(input);
|
|
||||||
Object.assign(this, data);
|
|
||||||
Object.freeze(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static from(input: UserEntitlementsInput): UserEntitlements {
|
|
||||||
return new UserEntitlements(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json: unknown): UserEntitlements {
|
|
||||||
return UserEntitlements.from(json as UserEntitlementsInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): UserEntitlementsData {
|
|
||||||
return UserEntitlementsSchema.parse(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user