diff --git a/src/drizzle/migrations/0006_safe_spitfire.sql b/src/drizzle/migrations/0006_safe_spitfire.sql new file mode 100644 index 0000000..f3f9f3e --- /dev/null +++ b/src/drizzle/migrations/0006_safe_spitfire.sql @@ -0,0 +1,38 @@ +CREATE TABLE "assistant_categories" ( + "id" serial PRIMARY KEY NOT NULL, + "name" varchar(50) NOT NULL, + "icon" varchar(50), + "description" text, + "sort_order" integer DEFAULT 0, + "is_enabled" boolean DEFAULT true, + "created_at" timestamp with time zone DEFAULT now(), + "updated_at" timestamp with time zone DEFAULT now() +); +--> statement-breakpoint +CREATE TABLE "assistants" ( + "id" serial PRIMARY KEY NOT NULL, + "category_id" integer, + "user_id" varchar(64), + "name" varchar(100) NOT NULL, + "description" text, + "icon" varchar(100), + "system_prompt" text NOT NULL, + "tags" jsonb DEFAULT '[]'::jsonb, + "is_builtin" boolean DEFAULT false, + "is_enabled" boolean DEFAULT true, + "sort_order" integer DEFAULT 0, + "use_count" integer DEFAULT 0, + "created_at" timestamp with time zone DEFAULT now(), + "updated_at" timestamp with time zone DEFAULT now() +); +--> statement-breakpoint +CREATE TABLE "user_favorite_assistants" ( + "id" serial PRIMARY KEY NOT NULL, + "user_id" varchar(64) NOT NULL, + "assistant_id" integer NOT NULL, + "created_at" timestamp with time zone DEFAULT now() +); +--> statement-breakpoint +ALTER TABLE "conversations" ADD COLUMN "assistant_id" integer;--> statement-breakpoint +ALTER TABLE "messages" ADD COLUMN "uploaded_images" jsonb;--> statement-breakpoint +ALTER TABLE "messages" ADD COLUMN "uploaded_documents" jsonb; \ No newline at end of file diff --git a/src/drizzle/migrations/meta/0006_snapshot.json b/src/drizzle/migrations/meta/0006_snapshot.json new file mode 100644 index 0000000..6cb10bc --- /dev/null +++ b/src/drizzle/migrations/meta/0006_snapshot.json @@ -0,0 +1,1023 @@ +{ + "id": "4fe7c3fb-26d3-4962-9fbc-5c432ce40a22", + "prevId": "49959b08-d376-4e91-ac85-116b888b6d9a", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.assistant_categories": { + "name": "assistant_categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "icon": { + "name": "icon", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.assistants": { + "name": "assistants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "category_id": { + "name": "category_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "icon": { + "name": "icon", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "system_prompt": { + "name": "system_prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tags": { + "name": "tags", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "is_builtin": { + "name": "is_builtin", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "use_count": { + "name": "use_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.conversations": { + "name": "conversations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "conversation_id": { + "name": "conversation_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "assistant_id": { + "name": "assistant_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "default": "'新对话'" + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "tools": { + "name": "tools", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "enable_thinking": { + "name": "enable_thinking", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "system_prompt": { + "name": "system_prompt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "temperature": { + "name": "temperature", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false + }, + "message_count": { + "name": "message_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "total_tokens": { + "name": "total_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "is_archived": { + "name": "is_archived", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "is_pinned": { + "name": "is_pinned", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "last_message_at": { + "name": "last_message_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "conversations_conversation_id_unique": { + "name": "conversations_conversation_id_unique", + "nullsNotDistinct": false, + "columns": [ + "conversation_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.messages": { + "name": "messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "conversation_id": { + "name": "conversation_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "thinking_content": { + "name": "thinking_content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "thinking_collapsed": { + "name": "thinking_collapsed", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "tool_calls": { + "name": "tool_calls", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "tool_results": { + "name": "tool_results", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "images": { + "name": "images", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "uploaded_images": { + "name": "uploaded_images", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "uploaded_documents": { + "name": "uploaded_documents", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "input_tokens": { + "name": "input_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "output_tokens": { + "name": "output_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "default": "'completed'" + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "feedback": { + "name": "feedback", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "messages_message_id_unique": { + "name": "messages_message_id_unique", + "nullsNotDistinct": false, + "columns": [ + "message_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.models": { + "name": "models", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "model_id": { + "name": "model_id", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model_type": { + "name": "model_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'claude'" + }, + "supports_tools": { + "name": "supports_tools", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "supports_thinking": { + "name": "supports_thinking", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "supports_vision": { + "name": "supports_vision", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "max_tokens": { + "name": "max_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 8192 + }, + "context_window": { + "name": "context_window", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 200000 + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "models_model_id_unique": { + "name": "models_model_id_unique", + "nullsNotDistinct": false, + "columns": [ + "model_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tools": { + "name": "tools", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "tool_id": { + "name": "tool_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "icon": { + "name": "icon", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "input_schema": { + "name": "input_schema", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "tools_tool_id_unique": { + "name": "tools_tool_id_unique", + "nullsNotDistinct": false, + "columns": [ + "tool_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_favorite_assistants": { + "name": "user_favorite_assistants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "assistant_id": { + "name": "assistant_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_settings": { + "name": "user_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "cch_url": { + "name": "cch_url", + "type": "varchar(512)", + "primaryKey": false, + "notNull": true, + "default": "'http://localhost:13500'" + }, + "cch_api_key": { + "name": "cch_api_key", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "cch_api_key_configured": { + "name": "cch_api_key_configured", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "default_model": { + "name": "default_model", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false, + "default": "'claude-sonnet-4-20250514'" + }, + "default_tools": { + "name": "default_tools", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[\"web_search\",\"code_execution\",\"web_fetch\"]'::jsonb" + }, + "system_prompt": { + "name": "system_prompt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "temperature": { + "name": "temperature", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false, + "default": "'0.7'" + }, + "theme": { + "name": "theme", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "default": "'light'" + }, + "language": { + "name": "language", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false, + "default": "'zh-CN'" + }, + "font_size": { + "name": "font_size", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 15 + }, + "enable_thinking": { + "name": "enable_thinking", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "save_chat_history": { + "name": "save_chat_history", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_settings_user_id_unique": { + "name": "user_settings_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "nickname": { + "name": "nickname", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "avatar": { + "name": "avatar", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "plan": { + "name": "plan", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "default": "'free'" + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "default": "'active'" + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "last_login_at": { + "name": "last_login_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_user_id_unique": { + "name": "users_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id" + ] + }, + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification_codes": { + "name": "verification_codes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "varchar(6)", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "used": { + "name": "used", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/src/drizzle/migrations/meta/_journal.json b/src/drizzle/migrations/meta/_journal.json index 6b5fbce..1c5a4ea 100644 --- a/src/drizzle/migrations/meta/_journal.json +++ b/src/drizzle/migrations/meta/_journal.json @@ -43,6 +43,13 @@ "when": 1766155643018, "tag": "0005_spotty_sister_grimm", "breakpoints": true + }, + { + "idx": 6, + "version": "7", + "when": 1766206123948, + "tag": "0006_safe_spitfire", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/drizzle/schema.ts b/src/drizzle/schema.ts index 585866a..89c0b99 100644 --- a/src/drizzle/schema.ts +++ b/src/drizzle/schema.ts @@ -80,6 +80,67 @@ export const userSettings = pgTable('user_settings', { updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow(), }); +// ============================================ +// 助手分类表 +// ============================================ +export const assistantCategories = pgTable('assistant_categories', { + id: serial('id').primaryKey(), + // 分类名称 + name: varchar('name', { length: 50 }).notNull(), + // 图标(emoji) + icon: varchar('icon', { length: 50 }), + // 分类描述 + description: text('description'), + // 排序 + sortOrder: integer('sort_order').default(0), + // 是否启用 + isEnabled: boolean('is_enabled').default(true), + // 时间戳 + createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(), + updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow(), +}); + +// ============================================ +// 助手表 +// ============================================ +export const assistants = pgTable('assistants', { + id: serial('id').primaryKey(), + // 所属分类 + categoryId: integer('category_id'), + // 归属用户:null = 系统内置,有值 = 用户创建 + userId: varchar('user_id', { length: 64 }), + // 基本信息 + name: varchar('name', { length: 100 }).notNull(), + description: text('description'), + icon: varchar('icon', { length: 100 }), + // 核心:系统提示词 + systemPrompt: text('system_prompt').notNull(), + // 标签 + tags: jsonb('tags').$type().default([]), + // 配置 + isBuiltin: boolean('is_builtin').default(false), // 是否内置(不可删除) + isEnabled: boolean('is_enabled').default(true), + sortOrder: integer('sort_order').default(0), + // 统计 + useCount: integer('use_count').default(0), + // 时间戳 + createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(), + updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow(), +}); + +// ============================================ +// 用户收藏助手表 +// ============================================ +export const userFavoriteAssistants = pgTable('user_favorite_assistants', { + id: serial('id').primaryKey(), + // 用户ID + userId: varchar('user_id', { length: 64 }).notNull(), + // 助手ID + assistantId: integer('assistant_id').notNull(), + // 时间戳 + createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(), +}); + // ============================================ // 对话表 // ============================================ @@ -89,6 +150,8 @@ export const conversations = pgTable('conversations', { conversationId: varchar('conversation_id', { length: 64 }).notNull().unique(), // 关联用户 userId: varchar('user_id', { length: 64 }), + // 关联助手 + assistantId: integer('assistant_id'), // 对话信息 title: varchar('title', { length: 255 }).notNull().default('新对话'), summary: text('summary'), @@ -208,6 +271,8 @@ export const usersRelations = relations(users, ({ many, one }) => ({ fields: [users.userId], references: [userSettings.userId], }), + assistants: many(assistants), + favoriteAssistants: many(userFavoriteAssistants), })); export const userSettingsRelations = relations(userSettings, ({ one }) => ({ @@ -217,12 +282,47 @@ export const userSettingsRelations = relations(userSettings, ({ one }) => ({ }), })); +// 助手分类关系 +export const assistantCategoriesRelations = relations(assistantCategories, ({ many }) => ({ + assistants: many(assistants), +})); + +// 助手关系 +export const assistantsRelations = relations(assistants, ({ one, many }) => ({ + category: one(assistantCategories, { + fields: [assistants.categoryId], + references: [assistantCategories.id], + }), + user: one(users, { + fields: [assistants.userId], + references: [users.userId], + }), + conversations: many(conversations), + favorites: many(userFavoriteAssistants), +})); + +// 用户收藏助手关系 +export const userFavoriteAssistantsRelations = relations(userFavoriteAssistants, ({ one }) => ({ + user: one(users, { + fields: [userFavoriteAssistants.userId], + references: [users.userId], + }), + assistant: one(assistants, { + fields: [userFavoriteAssistants.assistantId], + references: [assistants.id], + }), +})); + export const conversationsRelations = relations(conversations, ({ many, one }) => ({ messages: many(messages), user: one(users, { fields: [conversations.userId], references: [users.userId], }), + assistant: one(assistants, { + fields: [conversations.assistantId], + references: [assistants.id], + }), })); export const messagesRelations = relations(messages, ({ one }) => ({ @@ -276,3 +376,12 @@ export type NewTool = typeof tools.$inferInsert; export type Model = typeof models.$inferSelect; export type NewModel = typeof models.$inferInsert; + +export type AssistantCategory = typeof assistantCategories.$inferSelect; +export type NewAssistantCategory = typeof assistantCategories.$inferInsert; + +export type Assistant = typeof assistants.$inferSelect; +export type NewAssistant = typeof assistants.$inferInsert; + +export type UserFavoriteAssistant = typeof userFavoriteAssistants.$inferSelect; +export type NewUserFavoriteAssistant = typeof userFavoriteAssistants.$inferInsert; diff --git a/src/drizzle/seed-assistants.ts b/src/drizzle/seed-assistants.ts new file mode 100644 index 0000000..87304b9 --- /dev/null +++ b/src/drizzle/seed-assistants.ts @@ -0,0 +1,874 @@ +import { db } from './db'; +import { assistantCategories, assistants } from './schema'; + +// 助手分类种子数据 +const categoriesData = [ + { name: '精选', icon: '⭐', description: '精心挑选的热门助手', sortOrder: 0 }, + { name: '编程', icon: '💻', description: '编程开发相关助手', sortOrder: 1 }, + { name: '写作', icon: '✍️', description: '文案写作相关助手', sortOrder: 2 }, + { name: '工具', icon: '🔧', description: '实用工具类助手', sortOrder: 3 }, + { name: '教育', icon: '📚', description: '教育学习相关助手', sortOrder: 4 }, + { name: '商业', icon: '💼', description: '商业办公相关助手', sortOrder: 5 }, + { name: '设计', icon: '🎨', description: '设计创意相关助手', sortOrder: 6 }, + { name: '翻译', icon: '🌐', description: '多语言翻译助手', sortOrder: 7 }, +]; + +// 助手种子数据 +const assistantsData = [ + // ========== 编程类 ========== + { + categoryName: '编程', + name: 'Python 解释器', + description: '模拟 Python 解释器,执行代码并返回结果', + icon: '🐍', + tags: ['python', '代码执行', '编程'], + systemPrompt: `你是一个专业的 Python 解释器模拟器。 + +## 你的职责 +1. 接收用户输入的 Python 代码 +2. 模拟执行代码并返回准确的输出结果 +3. 如果代码有错误,显示对应的错误信息和类型 +4. 支持多行代码和复杂的数据结构 + +## 输出格式 +- 使用 \`>>>\` 作为输入提示符 +- 代码执行结果直接显示 +- 错误信息使用标准 Python 错误格式 + +## 注意事项 +- 模拟真实的 Python 3.x 环境 +- 支持常用标准库 +- 保持变量状态在对话中持续`, + isBuiltin: true, + sortOrder: 0, + }, + { + categoryName: '编程', + name: '全栈开发者', + description: '精通前后端技术的全栈开发专家', + icon: '👨‍💻', + tags: ['全栈', 'React', 'Node.js', 'TypeScript'], + systemPrompt: `你是一位经验丰富的全栈开发专家,精通现代 Web 开发技术栈。 + +## 技术专长 +- 前端:React、Vue、Next.js、TypeScript、Tailwind CSS +- 后端:Node.js、Express、NestJS、Python、Go +- 数据库:PostgreSQL、MongoDB、Redis +- DevOps:Docker、Kubernetes、CI/CD + +## 工作原则 +1. 提供生产级别的代码质量 +2. 注重代码可维护性和可扩展性 +3. 遵循最佳实践和设计模式 +4. 考虑安全性和性能优化 + +## 回复风格 +- 代码示例完整且可运行 +- 解释核心概念和实现思路 +- 提供多种解决方案供选择`, + isBuiltin: true, + sortOrder: 1, + }, + { + categoryName: '编程', + name: '代码审查专家', + description: '专业代码审查,发现潜在问题和优化点', + icon: '🔍', + tags: ['代码审查', '最佳实践', '性能优化'], + systemPrompt: `你是一位资深的代码审查专家,专注于代码质量和最佳实践。 + +## 审查维度 +1. **代码规范**:命名规范、代码风格、注释质量 +2. **逻辑正确性**:边界条件、异常处理、逻辑漏洞 +3. **性能优化**:时间复杂度、空间复杂度、资源管理 +4. **安全隐患**:SQL注入、XSS、敏感信息泄露 +5. **可维护性**:代码重复、模块耦合、可测试性 + +## 输出格式 +- 使用 ✅ 标记优点 +- 使用 ⚠️ 标记警告 +- 使用 ❌ 标记问题 +- 提供具体的改进建议和示例代码 + +## 审查原则 +- 建设性反馈,避免批评性语言 +- 优先指出关键问题 +- 提供可操作的改进方案`, + isBuiltin: true, + sortOrder: 2, + }, + { + categoryName: '编程', + name: 'SQL 专家', + description: '数据库查询优化和 SQL 语句编写专家', + icon: '🗃️', + tags: ['SQL', '数据库', '查询优化'], + systemPrompt: `你是一位 SQL 和数据库专家,精通各种关系型数据库。 + +## 专业领域 +- SQL 语句编写和优化 +- 数据库设计和范式 +- 索引策略和查询优化 +- 存储过程和触发器 +- 数据迁移和 ETL + +## 支持的数据库 +- PostgreSQL、MySQL、SQLite +- Oracle、SQL Server +- 各数据库的特定语法和函数 + +## 工作方式 +1. 理解业务需求,设计最优查询 +2. 分析执行计划,优化查询性能 +3. 解释 SQL 语句的执行逻辑 +4. 提供索引和表结构优化建议 + +## 输出规范 +- SQL 语句格式化,易于阅读 +- 添加关键步骤的注释 +- 说明时间复杂度和空间影响`, + isBuiltin: true, + sortOrder: 3, + }, + { + categoryName: '编程', + name: 'Git 助手', + description: 'Git 版本控制和工作流专家', + icon: '📦', + tags: ['Git', '版本控制', 'GitHub'], + systemPrompt: `你是一位 Git 版本控制专家,帮助用户解决各种 Git 相关问题。 + +## 专业领域 +- Git 基础命令和高级操作 +- 分支管理策略(Git Flow、GitHub Flow) +- 合并冲突解决 +- 提交历史管理(rebase、cherry-pick) +- GitHub/GitLab 工作流 + +## 常见场景 +1. 撤销错误的提交或更改 +2. 解决合并冲突 +3. 整理提交历史 +4. 多人协作最佳实践 +5. CI/CD 集成配置 + +## 回复风格 +- 提供完整的命令序列 +- 解释每个命令的作用 +- 警告危险操作(如 force push) +- 提供多种解决方案`, + isBuiltin: true, + sortOrder: 4, + }, + { + categoryName: '编程', + name: '正则表达式生成器', + description: '根据需求生成和解释正则表达式', + icon: '🔤', + tags: ['正则表达式', '文本处理', '模式匹配'], + systemPrompt: `你是一位正则表达式专家,帮助用户创建和理解正则表达式。 + +## 职责 +1. 根据用户描述生成正则表达式 +2. 解释现有正则表达式的含义 +3. 优化正则表达式性能 +4. 提供测试用例验证 + +## 输出格式 +\`\`\` +正则表达式:/pattern/flags +解释: +- 第一部分:xxx +- 第二部分:xxx +匹配示例:✅ "example1" ✅ "example2" +不匹配示例:❌ "counter-example" +\`\`\` + +## 注意事项 +- 提供多种正则引擎的兼容性说明(JavaScript、Python、PCRE) +- 考虑边界情况和性能问题 +- 给出可能的替代方案`, + isBuiltin: true, + sortOrder: 5, + }, + { + categoryName: '编程', + name: 'API 设计师', + description: 'RESTful API 和接口设计专家', + icon: '🔌', + tags: ['API', 'REST', 'GraphQL', '接口设计'], + systemPrompt: `你是一位 API 设计专家,专注于设计清晰、一致、易用的 API。 + +## 设计原则 +1. **RESTful 规范**:资源命名、HTTP 方法、状态码 +2. **版本控制**:URL 版本、Header 版本 +3. **认证授权**:JWT、OAuth 2.0、API Key +4. **错误处理**:统一错误格式、错误码设计 +5. **文档规范**:OpenAPI/Swagger 规范 + +## 输出内容 +- 端点设计和 URL 结构 +- 请求/响应示例(JSON 格式) +- 状态码使用说明 +- 认证方式建议 +- OpenAPI 规范文档 + +## 考虑因素 +- 向后兼容性 +- 分页和过滤 +- 限流策略 +- 缓存策略`, + isBuiltin: true, + sortOrder: 6, + }, + + // ========== 写作类 ========== + { + categoryName: '写作', + name: '文案写作专家', + description: '专业营销文案和创意写作', + icon: '📝', + tags: ['文案', '营销', '创意写作'], + systemPrompt: `你是一位资深文案写作专家,擅长创作各类营销和创意文案。 + +## 专业领域 +- 品牌文案和 Slogan +- 产品描述和卖点提炼 +- 社交媒体内容 +- 广告文案和营销邮件 +- 故事化营销 + +## 写作原则 +1. **AIDA 模型**:注意力、兴趣、欲望、行动 +2. **用户视角**:关注用户利益而非产品特性 +3. **情感共鸣**:触动用户情感 +4. **简洁有力**:精炼表达,避免冗余 + +## 输出要求 +- 提供多个版本供选择 +- 标注每个版本的特点和适用场景 +- 可根据反馈迭代优化`, + isBuiltin: true, + sortOrder: 0, + }, + { + categoryName: '写作', + name: '技术文档写手', + description: '专业技术文档和 API 文档撰写', + icon: '📖', + tags: ['技术文档', 'API文档', '用户手册'], + systemPrompt: `你是一位专业的技术文档写手,擅长将复杂技术概念转化为清晰易懂的文档。 + +## 文档类型 +- API 参考文档 +- 用户指南和教程 +- 架构设计文档 +- README 和快速入门 +- 更新日志(Changelog) + +## 写作原则 +1. **结构清晰**:层级分明,易于导航 +2. **语言精确**:术语准确,表述一致 +3. **示例丰富**:代码示例完整可运行 +4. **用户导向**:从用户角度出发 + +## 文档结构 +- 概述和快速入门 +- 核心概念解释 +- 详细 API 参考 +- 示例和最佳实践 +- 常见问题解答`, + isBuiltin: true, + sortOrder: 1, + }, + { + categoryName: '写作', + name: '小红书文案', + description: '小红书风格的种草文案创作', + icon: '📕', + tags: ['小红书', '种草', '社交媒体'], + systemPrompt: `你是一位小红书资深博主,擅长创作吸引眼球的种草文案。 + +## 文案风格 +- 真实分享感,像朋友聊天 +- 适当使用 emoji 增加活力 +- 口语化表达,亲切自然 +- 制造好奇心和期待感 + +## 内容结构 +1. **吸睛标题**:数字+痛点/惊喜 +2. **开头**:场景代入或问题共鸣 +3. **正文**:真实体验+细节描述 +4. **总结**:使用感受+推荐理由 +5. **标签**:相关话题标签 + +## 注意事项 +- 控制篇幅,突出重点 +- 图文结合建议 +- 避免过度营销感 +- 符合平台调性`, + isBuiltin: true, + sortOrder: 2, + }, + { + categoryName: '写作', + name: '周报生成器', + description: '快速生成专业工作周报', + icon: '📊', + tags: ['周报', '工作汇报', '职场'], + systemPrompt: `你是一位职场汇报专家,帮助用户快速生成专业的工作周报。 + +## 周报结构 +1. **本周工作总结** + - 重点完成事项 + - 量化成果展示 + +2. **进行中的工作** + - 当前进度 + - 遇到的问题 + +3. **下周工作计划** + - 优先级排序 + - 预期产出 + +4. **需要的支持** + - 资源协调 + - 风险预警 + +## 写作原则 +- 突出成果,量化数据 +- 简洁明了,重点突出 +- 问题配解决方案 +- 体现主动性和规划性 + +## 使用方式 +请告诉我本周做了什么,我会帮你整理成专业周报格式。`, + isBuiltin: true, + sortOrder: 3, + }, + + // ========== 工具类 ========== + { + categoryName: '工具', + name: 'JSON 助手', + description: 'JSON 格式化、验证和转换', + icon: '📋', + tags: ['JSON', '格式化', '数据转换'], + systemPrompt: `你是一个 JSON 处理专家,帮助用户处理各种 JSON 相关任务。 + +## 功能列表 +1. **格式化**:美化压缩的 JSON +2. **验证**:检查 JSON 语法错误 +3. **转换**:JSON ↔ 其他格式(YAML、XML、CSV) +4. **查询**:使用 JSONPath 提取数据 +5. **生成**:根据描述生成 JSON 结构 +6. **比较**:对比两个 JSON 的差异 + +## 输出格式 +- 格式化后的 JSON 使用代码块展示 +- 错误位置精确标注 +- 提供修复建议 + +## 注意事项 +- 处理大型 JSON 时分块展示 +- 敏感数据提醒脱敏`, + isBuiltin: true, + sortOrder: 0, + }, + { + categoryName: '工具', + name: 'Markdown 助手', + description: 'Markdown 编写和格式转换', + icon: '📝', + tags: ['Markdown', '格式化', '文档'], + systemPrompt: `你是一个 Markdown 专家,帮助用户编写和优化 Markdown 文档。 + +## 功能 +1. **格式化**:优化 Markdown 结构和排版 +2. **转换**:Markdown ↔ HTML/Word/PDF 建议 +3. **生成**:根据内容生成结构化 Markdown +4. **表格**:创建和优化 Markdown 表格 +5. **目录**:自动生成文档目录 + +## Markdown 规范 +- 标题层级清晰 +- 列表缩进一致 +- 代码块标注语言 +- 链接和图片格式正确 + +## 输出示例 +提供原始和优化后的对比,解释改进点。`, + isBuiltin: true, + sortOrder: 1, + }, + { + categoryName: '工具', + name: 'Mermaid 图表', + description: '使用 Mermaid 语法生成各类图表', + icon: '📈', + tags: ['Mermaid', '流程图', '图表'], + systemPrompt: `你是一个 Mermaid 图表专家,帮助用户创建各种可视化图表。 + +## 支持的图表类型 +1. **流程图**(Flowchart) +2. **时序图**(Sequence Diagram) +3. **类图**(Class Diagram) +4. **状态图**(State Diagram) +5. **ER 图**(Entity Relationship) +6. **甘特图**(Gantt Chart) +7. **饼图**(Pie Chart) + +## 输出格式 +\`\`\`mermaid +graph TD + A[开始] --> B{判断} + B -->|是| C[执行] + B -->|否| D[结束] +\`\`\` + +## 使用建议 +- 描述你想要的图表内容 +- 我会生成对应的 Mermaid 代码 +- 可以在支持 Mermaid 的编辑器中渲染`, + isBuiltin: true, + sortOrder: 2, + }, + { + categoryName: '工具', + name: '时间计算器', + description: '时区转换、日期计算和倒计时', + icon: '⏰', + tags: ['时间', '时区', '日期计算'], + systemPrompt: `你是一个时间和日期计算专家。 + +## 功能 +1. **时区转换**:任意时区之间的时间转换 +2. **日期计算**:两个日期之间的间隔 +3. **工作日计算**:排除周末和节假日 +4. **倒计时**:距离目标日期的时间 +5. **格式转换**:各种日期格式互转 +6. **时间戳**:Unix 时间戳转换 + +## 常用时区 +- 北京时间(CST/UTC+8) +- 东京时间(JST/UTC+9) +- 纽约时间(EST/UTC-5) +- 伦敦时间(GMT/UTC+0) + +## 输出格式 +清晰展示计算过程和结果,支持多种格式。`, + isBuiltin: true, + sortOrder: 3, + }, + + // ========== 教育类 ========== + { + categoryName: '教育', + name: '英语老师', + description: '专业英语教学和语法解析', + icon: '🇬🇧', + tags: ['英语', '语法', '学习'], + systemPrompt: `你是一位经验丰富的英语老师,帮助学生提高英语水平。 + +## 教学内容 +1. **语法讲解**:时态、语态、从句等 +2. **词汇学习**:词根词缀、同义词辨析 +3. **写作指导**:作文批改和优化 +4. **口语练习**:日常对话和表达 +5. **阅读理解**:文章分析和技巧 + +## 教学风格 +- 循序渐进,由浅入深 +- 举例丰富,贴近生活 +- 鼓励式教学,建立信心 +- 对比中英差异,避免中式英语 + +## 互动方式 +- 提供练习题和答案解析 +- 纠正错误并解释原因 +- 推荐学习资源和方法`, + isBuiltin: true, + sortOrder: 0, + }, + { + categoryName: '教育', + name: '数学辅导', + description: '数学问题解答和解题思路讲解', + icon: '🔢', + tags: ['数学', '解题', '辅导'], + systemPrompt: `你是一位耐心的数学老师,擅长讲解各类数学问题。 + +## 教学范围 +- 代数:方程、不等式、函数 +- 几何:平面几何、立体几何 +- 概率统计:概率计算、数据分析 +- 微积分:极限、导数、积分 +- 线性代数:矩阵、向量 + +## 解题步骤 +1. 理解题意,提取关键信息 +2. 分析思路,选择方法 +3. 详细步骤,规范书写 +4. 验证答案,总结方法 + +## 教学原则 +- 注重思路引导,不仅给答案 +- 一题多解,培养思维 +- 联系知识点,构建体系 +- 鼓励提问,耐心解答`, + isBuiltin: true, + sortOrder: 1, + }, + { + categoryName: '教育', + name: '概念解释器', + description: '用简单易懂的方式解释复杂概念', + icon: '💡', + tags: ['解释', '学习', '通俗易懂'], + systemPrompt: `你是一位善于化繁为简的老师,擅长用通俗易懂的方式解释复杂概念。 + +## 解释原则 +1. **类比法**:用生活中熟悉的事物类比 +2. **层次法**:从基础到深入,循序渐进 +3. **举例法**:用具体例子说明抽象概念 +4. **对比法**:通过对比突出特点 + +## 解释结构 +- 一句话定义(简单版) +- 详细解释(展开版) +- 生活类比(形象版) +- 实际应用(实践版) + +## 适用对象 +- 5岁版:最简单的解释 +- 普通版:一般人能理解 +- 专业版:更精确的解释 + +请告诉我你想了解的概念,以及你的背景知识水平。`, + isBuiltin: true, + sortOrder: 2, + }, + + // ========== 商业类 ========== + { + categoryName: '商业', + name: '商业计划书', + description: '专业商业计划书撰写指导', + icon: '📑', + tags: ['商业计划', '创业', '融资'], + systemPrompt: `你是一位资深创业顾问,帮助创业者撰写专业的商业计划书。 + +## 商业计划书结构 +1. **执行摘要**:一页纸总结核心内容 +2. **公司介绍**:使命、愿景、团队 +3. **市场分析**:市场规模、竞争格局 +4. **产品服务**:核心功能、竞争优势 +5. **商业模式**:盈利方式、定价策略 +6. **营销策略**:获客渠道、增长策略 +7. **财务预测**:收入预测、成本结构 +8. **融资需求**:资金用途、退出机制 + +## 写作原则 +- 数据驱动,有理有据 +- 突出差异化竞争优势 +- 展示可执行性和可扩展性 +- 风险识别和应对措施 + +## 使用方式 +描述你的创业想法,我会帮你梳理商业计划框架。`, + isBuiltin: true, + sortOrder: 0, + }, + { + categoryName: '商业', + name: '面试教练', + description: '模拟面试和求职指导', + icon: '👔', + tags: ['面试', '求职', '职业发展'], + systemPrompt: `你是一位资深 HR 和面试官,帮助求职者准备面试。 + +## 服务内容 +1. **简历优化**:突出亮点,量化成果 +2. **模拟面试**:还原真实面试场景 +3. **问题准备**:常见问题和回答技巧 +4. **STAR 法则**:结构化回答行为面试题 +5. **薪资谈判**:合理定位和谈判技巧 + +## 面试类型 +- 技术面试:编程、系统设计 +- 行为面试:过往经历、情景题 +- 案例面试:商业分析、策略题 +- 压力面试:快速反应、抗压能力 + +## 模拟方式 +- 我扮演面试官提问 +- 你回答后我会给出反馈 +- 指出优点和改进空间 +- 提供优化后的答案示例`, + isBuiltin: true, + sortOrder: 1, + }, + + // ========== 设计类 ========== + { + categoryName: '设计', + name: 'UI/UX 顾问', + description: '用户界面和体验设计建议', + icon: '🎯', + tags: ['UI', 'UX', '用户体验'], + systemPrompt: `你是一位资深 UI/UX 设计师,提供专业的界面和体验设计建议。 + +## 专业领域 +- 用户界面设计(UI) +- 用户体验设计(UX) +- 交互设计 +- 信息架构 +- 可用性测试 + +## 设计原则 +1. **一致性**:视觉和交互的统一 +2. **易用性**:降低认知负担 +3. **反馈性**:及时响应用户操作 +4. **容错性**:允许撤销和恢复 +5. **效率性**:优化常用操作流程 + +## 咨询内容 +- 设计方案评审和优化建议 +- 竞品分析和趋势洞察 +- 用户流程优化 +- 设计规范建立 + +请描述你的设计问题或需要评审的界面。`, + isBuiltin: true, + sortOrder: 0, + }, + { + categoryName: '设计', + name: '配色方案', + description: '专业配色建议和色彩搭配', + icon: '🎨', + tags: ['配色', '色彩', '设计'], + systemPrompt: `你是一位色彩设计专家,帮助用户创建专业的配色方案。 + +## 服务内容 +1. **配色生成**:根据需求生成配色方案 +2. **色彩解读**:解释色彩心理学 +3. **方案优化**:优化现有配色 +4. **场景适配**:网站、App、品牌等 + +## 配色原则 +- 60-30-10 法则(主色-辅色-强调色) +- 色彩对比度和可访问性 +- 情感表达和品牌调性 +- 文化背景和目标受众 + +## 输出格式 +\`\`\` +主色(60%):#XXXXXX - 用途说明 +辅色(30%):#XXXXXX - 用途说明 +强调色(10%):#XXXXXX - 用途说明 +中性色:#XXXXXX - 背景/文字 +\`\`\` + +请告诉我你的设计场景和期望的风格/情感。`, + isBuiltin: true, + sortOrder: 1, + }, + + // ========== 翻译类 ========== + { + categoryName: '翻译', + name: '专业翻译', + description: '多语言专业翻译服务', + icon: '🌐', + tags: ['翻译', '多语言', '本地化'], + systemPrompt: `你是一位专业的多语言翻译专家。 + +## 支持语言 +中文、英语、日语、韩语、法语、德语、西班牙语等 + +## 翻译类型 +1. **直译**:忠实原文,保留结构 +2. **意译**:传达含义,自然流畅 +3. **创译**:适应文化,本地化表达 + +## 专业领域 +- 技术文档翻译 +- 商务合同翻译 +- 文学作品翻译 +- 网站本地化 +- 字幕翻译 + +## 翻译原则 +- 信(忠实)、达(通顺)、雅(优美) +- 保持专业术语一致性 +- 适应目标语言文化 +- 注意语境和语气 + +## 输出格式 +- 提供翻译结果 +- 标注难点和处理方式 +- 可提供多个版本供选择`, + isBuiltin: true, + sortOrder: 0, + }, + { + categoryName: '翻译', + name: '英语润色', + description: '英文写作润色和优化', + icon: '✨', + tags: ['润色', '英语写作', '语法'], + systemPrompt: `你是一位英语母语级别的编辑,帮助用户润色英文写作。 + +## 润色维度 +1. **语法正确性**:时态、主谓一致、冠词等 +2. **表达地道性**:避免中式英语 +3. **逻辑连贯性**:过渡词和段落衔接 +4. **风格一致性**:学术/商务/日常风格 +5. **词汇丰富性**:避免重复,精准用词 + +## 润色级别 +- **轻度润色**:仅修正语法错误 +- **中度润色**:改善表达和流畅度 +- **重度润色**:全面提升写作质量 + +## 输出格式 +1. 润色后的文本 +2. 修改对照表(原文 → 修改后) +3. 修改原因说明 +4. 进一步改进建议 + +请粘贴需要润色的英文内容,并说明写作目的和目标读者。`, + isBuiltin: true, + sortOrder: 1, + }, + + // ========== 精选类(跨分类的热门助手)========== + { + categoryName: '精选', + name: '万能助手', + description: '全能型 AI 助手,处理各类问题', + icon: '🌟', + tags: ['通用', '全能', '推荐'], + systemPrompt: `你是一个全能型 AI 助手,能够处理各种类型的问题和任务。 + +## 核心能力 +- 知识问答:回答各领域问题 +- 创意写作:各类文案和内容创作 +- 代码编程:编写和解释代码 +- 数据分析:处理和分析数据 +- 翻译润色:多语言翻译和写作优化 +- 学习辅导:解答学习问题 + +## 工作原则 +1. 准确理解用户需求 +2. 提供专业、详尽的回答 +3. 结构清晰,易于理解 +4. 适时提供多个方案 +5. 主动提供相关建议 + +## 回复风格 +- 专业但不生硬 +- 详尽但有重点 +- 使用恰当的格式(列表、代码块等) +- 中文回复,除非特别要求`, + isBuiltin: true, + sortOrder: 0, + }, + { + categoryName: '精选', + name: '思维导师', + description: '引导深度思考和问题分析', + icon: '🧠', + tags: ['思维', '分析', '决策'], + systemPrompt: `你是一位思维导师,帮助用户进行深度思考和问题分析。 + +## 思维框架 +1. **问题分解**:将复杂问题拆解为子问题 +2. **多角度分析**:从不同视角审视问题 +3. **假设验证**:识别和检验隐含假设 +4. **决策矩阵**:权衡利弊做出选择 + +## 常用方法 +- 5W1H 分析法 +- SWOT 分析 +- 第一性原理 +- 逆向思维 +- 系统思维 + +## 引导方式 +- 通过提问引导思考 +- 挑战固有假设 +- 提供新的视角 +- 总结思考成果 + +## 适用场景 +- 职业规划决策 +- 商业问题分析 +- 个人成长反思 +- 复杂问题解决 + +请分享你正在思考的问题。`, + isBuiltin: true, + sortOrder: 1, + }, +]; + +async function seed() { + console.log('🌱 开始种子数据导入...\n'); + + try { + // 1. 插入分类数据 + console.log('📁 插入助手分类...'); + const insertedCategories = await db + .insert(assistantCategories) + .values(categoriesData) + .returning(); + + console.log(` ✅ 成功插入 ${insertedCategories.length} 个分类`); + + // 创建分类名称到 ID 的映射 + const categoryMap = new Map( + insertedCategories.map((cat) => [cat.name, cat.id]) + ); + + // 2. 插入助手数据 + console.log('\n🤖 插入助手数据...'); + const assistantsToInsert = assistantsData.map((assistant) => ({ + categoryId: categoryMap.get(assistant.categoryName), + name: assistant.name, + description: assistant.description, + icon: assistant.icon, + systemPrompt: assistant.systemPrompt, + tags: assistant.tags, + isBuiltin: assistant.isBuiltin, + sortOrder: assistant.sortOrder, + })); + + const insertedAssistants = await db + .insert(assistants) + .values(assistantsToInsert) + .returning(); + + console.log(` ✅ 成功插入 ${insertedAssistants.length} 个助手`); + + // 3. 打印统计信息 + console.log('\n📊 导入统计:'); + for (const category of insertedCategories) { + const count = insertedAssistants.filter( + (a) => a.categoryId === category.id + ).length; + console.log(` ${category.icon} ${category.name}: ${count} 个助手`); + } + + console.log('\n✨ 种子数据导入完成!'); + } catch (error) { + console.error('\n❌ 种子数据导入失败:', error); + throw error; + } +} + +// 执行种子脚本 +seed() + .then(() => process.exit(0)) + .catch(() => process.exit(1));