93 lines
2.8 KiB
TypeScript
93 lines
2.8 KiB
TypeScript
/**
|
||
* LocalMessage 模型(存储层副本)
|
||
*
|
||
* 与 `src/data/models/chat/chat_message.ts` 的 `ChatMessage` 解耦:
|
||
* 存储层维护自己的数据形态(独立 id/role/content/createdAt + 内部 sessionId 分组),
|
||
* 不耦合 wire 模型,方便后续 schema 独立演进。
|
||
*
|
||
* 对齐项目内其他 Zod 模型的范式:
|
||
* - `*Schema` 是验证与默认值的单一来源
|
||
* - `*Input` 是构造输入(字段可缺),`*Data` 是解析后形态
|
||
* - 不可变(private constructor + Object.freeze)
|
||
* - 桥接方法 `toRow` / `fromRow` 处理 Dexie 行 ↔ 类实例的转换
|
||
*/
|
||
|
||
import { z } from "zod";
|
||
import { ChatImageSchema, ChatLockDetailSchema } from "@/data/schemas/chat";
|
||
import type { LocalMessageRow } from "./local_chat_db";
|
||
|
||
export const LocalMessageSchema = z.object({
|
||
id: z.string(),
|
||
role: z.string(),
|
||
type: z.string().default("text"),
|
||
content: z.string(),
|
||
createdAt: z.string().default(""),
|
||
audioUrl: z.string().nullable().default(null),
|
||
image: ChatImageSchema,
|
||
lockDetail: ChatLockDetailSchema,
|
||
sessionId: z.string().default(""),
|
||
});
|
||
|
||
export type LocalMessageInput = z.input<typeof LocalMessageSchema>;
|
||
export type LocalMessageData = z.output<typeof LocalMessageSchema>;
|
||
|
||
export class LocalMessage {
|
||
declare readonly id: string;
|
||
declare readonly role: string;
|
||
declare readonly type: string;
|
||
declare readonly content: string;
|
||
declare readonly createdAt: string;
|
||
declare readonly audioUrl: string | null;
|
||
declare readonly image: z.output<typeof ChatImageSchema>;
|
||
declare readonly lockDetail: z.output<typeof ChatLockDetailSchema>;
|
||
declare readonly sessionId: string;
|
||
|
||
private constructor(input: LocalMessageInput) {
|
||
const data = LocalMessageSchema.parse(input);
|
||
Object.assign(this, data);
|
||
Object.freeze(this);
|
||
}
|
||
|
||
static from(input: LocalMessageInput): LocalMessage {
|
||
return new LocalMessage(input);
|
||
}
|
||
|
||
static fromJson(json: unknown): LocalMessage {
|
||
return LocalMessage.from(json as LocalMessageInput);
|
||
}
|
||
|
||
toJson(): LocalMessageData {
|
||
return LocalMessageSchema.parse(this);
|
||
}
|
||
|
||
/** 转成 Dexie 行(不含 dbId,让 Dexie 自增)。 */
|
||
toRow(): Omit<LocalMessageRow, "dbId"> {
|
||
return {
|
||
id: this.id,
|
||
role: this.role,
|
||
type: this.type,
|
||
content: this.content,
|
||
createdAt: this.createdAt,
|
||
audioUrl: this.audioUrl,
|
||
image: this.image,
|
||
lockDetail: this.lockDetail,
|
||
sessionId: this.sessionId,
|
||
};
|
||
}
|
||
|
||
/** 从 Dexie 行还原(忽略 dbId 内部主键)。 */
|
||
static fromRow(row: LocalMessageRow): LocalMessage {
|
||
return LocalMessage.from({
|
||
id: row.id,
|
||
role: row.role,
|
||
type: row.type,
|
||
content: row.content,
|
||
createdAt: row.createdAt,
|
||
audioUrl: row.audioUrl ?? null,
|
||
image: row.image,
|
||
lockDetail: row.lockDetail,
|
||
sessionId: row.sessionId,
|
||
});
|
||
}
|
||
}
|