You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
144 lines
4.8 KiB
144 lines
4.8 KiB
import { dbGlobal } from "drizzle-pkg/lib/db";
|
|
import { characterCards } from "drizzle-pkg/lib/schema/character";
|
|
import { eq, and, asc, sql } from "drizzle-orm";
|
|
|
|
export type CharacterCardRow = typeof characterCards.$inferSelect;
|
|
|
|
export interface CreateCharacterCardInput {
|
|
slug: string;
|
|
name: string;
|
|
avatar?: string | null;
|
|
description?: string | null;
|
|
personality?: string | null;
|
|
scenario?: string | null;
|
|
firstMessage?: string | null;
|
|
exampleDialogue?: string | null;
|
|
systemPrompt?: string;
|
|
defaultModelId?: number | null;
|
|
tags?: string | null;
|
|
isPublic?: number;
|
|
enabled?: number;
|
|
sortOrder?: number;
|
|
}
|
|
|
|
export interface UpdateCharacterCardInput {
|
|
slug?: string;
|
|
name?: string;
|
|
avatar?: string | null;
|
|
description?: string | null;
|
|
personality?: string | null;
|
|
scenario?: string | null;
|
|
firstMessage?: string | null;
|
|
exampleDialogue?: string | null;
|
|
systemPrompt?: string;
|
|
defaultModelId?: number | null;
|
|
tags?: string | null;
|
|
isPublic?: number;
|
|
enabled?: number;
|
|
sortOrder?: number;
|
|
}
|
|
|
|
export async function getCharacterCardBySlug(slug: string): Promise<CharacterCardRow | null> {
|
|
const [row] = await dbGlobal
|
|
.select()
|
|
.from(characterCards)
|
|
.where(and(eq(characterCards.slug, slug), eq(characterCards.enabled, 1)))
|
|
.limit(1);
|
|
return row ?? null;
|
|
}
|
|
|
|
export async function getCharacterCardBySlugAny(slug: string): Promise<CharacterCardRow | null> {
|
|
const [row] = await dbGlobal
|
|
.select()
|
|
.from(characterCards)
|
|
.where(eq(characterCards.slug, slug))
|
|
.limit(1);
|
|
return row ?? null;
|
|
}
|
|
|
|
export async function getCharacterCardById(id: number): Promise<CharacterCardRow | null> {
|
|
const [row] = await dbGlobal
|
|
.select()
|
|
.from(characterCards)
|
|
.where(eq(characterCards.id, id))
|
|
.limit(1);
|
|
return row ?? null;
|
|
}
|
|
|
|
export async function listCharacterCards(onlyEnabled = true): Promise<CharacterCardRow[]> {
|
|
const where = onlyEnabled ? eq(characterCards.enabled, 1) : sql`1=1`;
|
|
return dbGlobal
|
|
.select()
|
|
.from(characterCards)
|
|
.where(where)
|
|
.orderBy(asc(characterCards.sortOrder), asc(characterCards.id));
|
|
}
|
|
|
|
export async function createCharacterCard(input: CreateCharacterCardInput): Promise<CharacterCardRow> {
|
|
const existing = await getCharacterCardBySlugAny(input.slug);
|
|
if (existing) {
|
|
throw new Error(`slug 已存在: ${input.slug}`);
|
|
}
|
|
const [row] = await dbGlobal
|
|
.insert(characterCards)
|
|
.values({
|
|
slug: input.slug,
|
|
name: input.name,
|
|
avatar: input.avatar ?? null,
|
|
description: input.description ?? null,
|
|
personality: input.personality ?? null,
|
|
scenario: input.scenario ?? null,
|
|
firstMessage: input.firstMessage ?? null,
|
|
exampleDialogue: input.exampleDialogue ?? null,
|
|
systemPrompt: input.systemPrompt ?? "",
|
|
defaultModelId: input.defaultModelId ?? null,
|
|
tags: input.tags ?? null,
|
|
isPublic: input.isPublic ?? 1,
|
|
enabled: input.enabled ?? 1,
|
|
sortOrder: input.sortOrder ?? 0,
|
|
})
|
|
.returning();
|
|
if (!row) throw new Error("创建角色卡失败");
|
|
return row;
|
|
}
|
|
|
|
export async function updateCharacterCard(
|
|
id: number,
|
|
input: UpdateCharacterCardInput,
|
|
): Promise<CharacterCardRow | null> {
|
|
const existing = await getCharacterCardById(id);
|
|
if (!existing) return null;
|
|
|
|
if (input.slug && input.slug !== existing.slug) {
|
|
const conflict = await getCharacterCardBySlugAny(input.slug);
|
|
if (conflict) {
|
|
throw new Error(`slug 已存在: ${input.slug}`);
|
|
}
|
|
}
|
|
|
|
const updates: Partial<CharacterCardRow> = {};
|
|
if (input.slug !== undefined) updates.slug = input.slug;
|
|
if (input.name !== undefined) updates.name = input.name;
|
|
if (input.avatar !== undefined) updates.avatar = input.avatar;
|
|
if (input.description !== undefined) updates.description = input.description;
|
|
if (input.personality !== undefined) updates.personality = input.personality;
|
|
if (input.scenario !== undefined) updates.scenario = input.scenario;
|
|
if (input.firstMessage !== undefined) updates.firstMessage = input.firstMessage;
|
|
if (input.exampleDialogue !== undefined) updates.exampleDialogue = input.exampleDialogue;
|
|
if (input.systemPrompt !== undefined) updates.systemPrompt = input.systemPrompt;
|
|
if (input.defaultModelId !== undefined) updates.defaultModelId = input.defaultModelId;
|
|
if (input.tags !== undefined) updates.tags = input.tags;
|
|
if (input.isPublic !== undefined) updates.isPublic = input.isPublic;
|
|
if (input.enabled !== undefined) updates.enabled = input.enabled;
|
|
if (input.sortOrder !== undefined) updates.sortOrder = input.sortOrder;
|
|
|
|
if (Object.keys(updates).length > 0) {
|
|
await dbGlobal.update(characterCards).set(updates).where(eq(characterCards.id, id));
|
|
}
|
|
|
|
return getCharacterCardById(id);
|
|
}
|
|
|
|
export async function deleteCharacterCard(id: number): Promise<void> {
|
|
await dbGlobal.delete(characterCards).where(eq(characterCards.id, id));
|
|
}
|
|
|