Files
cozsweet-frontend-nextjs/src/data/storage/chat/local_message.ts
T

93 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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,
});
}
}