- 新增 uploadedImages 字段存储用户上传的图片(Base64编码数组) - 新增 uploadedDocuments 字段存储用户上传的文档数据 - 定义 UploadedDocumentData 接口,包含文档名称、大小、类型和内容
279 lines
10 KiB
TypeScript
279 lines
10 KiB
TypeScript
import {
|
||
pgTable,
|
||
serial,
|
||
varchar,
|
||
text,
|
||
boolean,
|
||
integer,
|
||
timestamp,
|
||
jsonb,
|
||
} from 'drizzle-orm/pg-core';
|
||
import { relations } from 'drizzle-orm';
|
||
|
||
// ============================================
|
||
// 用户表
|
||
// ============================================
|
||
export const users = pgTable('users', {
|
||
id: serial('id').primaryKey(),
|
||
// 用户唯一标识
|
||
userId: varchar('user_id', { length: 64 }).notNull().unique(),
|
||
// 基本信息
|
||
email: varchar('email', { length: 255 }).notNull().unique(),
|
||
password: varchar('password', { length: 255 }).notNull(),
|
||
nickname: varchar('nickname', { length: 64 }).notNull(),
|
||
avatar: varchar('avatar', { length: 512 }),
|
||
// 套餐和状态
|
||
plan: varchar('plan', { length: 20 }).default('free'), // free, pro, enterprise
|
||
status: varchar('status', { length: 20 }).default('active'), // active, inactive, banned
|
||
emailVerified: boolean('email_verified').default(false),
|
||
// 登录信息
|
||
lastLoginAt: timestamp('last_login_at', { withTimezone: true }),
|
||
// 时间戳
|
||
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(),
|
||
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow(),
|
||
});
|
||
|
||
// ============================================
|
||
// 验证码表
|
||
// ============================================
|
||
export const verificationCodes = pgTable('verification_codes', {
|
||
id: serial('id').primaryKey(),
|
||
// 邮箱
|
||
email: varchar('email', { length: 255 }).notNull(),
|
||
// 验证码
|
||
code: varchar('code', { length: 6 }).notNull(),
|
||
// 类型: register(注册), login(登录), reset(重置密码)
|
||
type: varchar('type', { length: 20 }).notNull(),
|
||
// 过期时间
|
||
expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
|
||
// 是否已使用
|
||
used: boolean('used').default(false),
|
||
// 创建时间
|
||
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(),
|
||
});
|
||
|
||
// ============================================
|
||
// 用户设置表
|
||
// ============================================
|
||
export const userSettings = pgTable('user_settings', {
|
||
id: serial('id').primaryKey(),
|
||
// 关联用户
|
||
userId: varchar('user_id', { length: 64 }).unique(),
|
||
// CCH 配置
|
||
cchUrl: varchar('cch_url', { length: 512 }).notNull().default('http://localhost:13500'),
|
||
cchApiKey: varchar('cch_api_key', { length: 512 }),
|
||
cchApiKeyConfigured: boolean('cch_api_key_configured').default(false),
|
||
// 默认设置
|
||
defaultModel: varchar('default_model', { length: 64 }).default('claude-sonnet-4-20250514'),
|
||
defaultTools: jsonb('default_tools').$type<string[]>().default(['web_search', 'code_execution', 'web_fetch']),
|
||
// AI 行为设置
|
||
systemPrompt: text('system_prompt'), // 系统提示词
|
||
temperature: varchar('temperature', { length: 10 }).default('0.7'), // 温度参数 (0-1)
|
||
// 偏好设置
|
||
theme: varchar('theme', { length: 20 }).default('light'),
|
||
language: varchar('language', { length: 10 }).default('zh-CN'),
|
||
fontSize: integer('font_size').default(15), // 全局字体大小 (12-20)
|
||
enableThinking: boolean('enable_thinking').default(false),
|
||
saveChatHistory: boolean('save_chat_history').default(true),
|
||
// 时间戳
|
||
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(),
|
||
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow(),
|
||
});
|
||
|
||
// ============================================
|
||
// 对话表
|
||
// ============================================
|
||
export const conversations = pgTable('conversations', {
|
||
id: serial('id').primaryKey(),
|
||
// 对话唯一标识
|
||
conversationId: varchar('conversation_id', { length: 64 }).notNull().unique(),
|
||
// 关联用户
|
||
userId: varchar('user_id', { length: 64 }),
|
||
// 对话信息
|
||
title: varchar('title', { length: 255 }).notNull().default('新对话'),
|
||
summary: text('summary'),
|
||
// 模型和工具配置
|
||
model: varchar('model', { length: 64 }).notNull(),
|
||
tools: jsonb('tools').$type<string[]>().default([]),
|
||
enableThinking: boolean('enable_thinking').default(false),
|
||
// AI 行为设置(对话级别,可覆盖全局设置)
|
||
systemPrompt: text('system_prompt'), // 对话专属系统提示词
|
||
temperature: varchar('temperature', { length: 10 }), // 对话专属温度参数
|
||
// 统计信息
|
||
messageCount: integer('message_count').default(0),
|
||
totalTokens: integer('total_tokens').default(0),
|
||
// 状态
|
||
isArchived: boolean('is_archived').default(false),
|
||
isPinned: boolean('is_pinned').default(false),
|
||
// 时间戳
|
||
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(),
|
||
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow(),
|
||
lastMessageAt: timestamp('last_message_at', { withTimezone: true }).defaultNow(),
|
||
});
|
||
|
||
// ============================================
|
||
// 消息表
|
||
// ============================================
|
||
export const messages = pgTable('messages', {
|
||
id: serial('id').primaryKey(),
|
||
// 消息唯一标识
|
||
messageId: varchar('message_id', { length: 64 }).notNull().unique(),
|
||
// 关联对话
|
||
conversationId: varchar('conversation_id', { length: 64 }).notNull(),
|
||
// 消息内容
|
||
role: varchar('role', { length: 20 }).notNull(), // user, assistant, system
|
||
content: text('content').notNull(),
|
||
// 思考内容
|
||
thinkingContent: text('thinking_content'),
|
||
thinkingCollapsed: boolean('thinking_collapsed').default(true),
|
||
// 工具调用记录
|
||
toolCalls: jsonb('tool_calls').$type<ToolCall[]>(),
|
||
toolResults: jsonb('tool_results').$type<ToolResult[]>(),
|
||
// 代码执行产生的图片(Base64 编码数组)
|
||
images: jsonb('images').$type<string[]>(),
|
||
// 用户上传的图片(Base64 编码数组)
|
||
uploadedImages: jsonb('uploaded_images').$type<string[]>(),
|
||
// 用户上传的文档
|
||
uploadedDocuments: jsonb('uploaded_documents').$type<UploadedDocumentData[]>(),
|
||
// Token 统计
|
||
inputTokens: integer('input_tokens').default(0),
|
||
outputTokens: integer('output_tokens').default(0),
|
||
// 状态
|
||
status: varchar('status', { length: 20 }).default('completed'), // pending, streaming, completed, error
|
||
errorMessage: text('error_message'),
|
||
// 用户反馈
|
||
feedback: varchar('feedback', { length: 10 }), // thumbs_up, thumbs_down
|
||
// 时间戳
|
||
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(),
|
||
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow(),
|
||
});
|
||
|
||
// ============================================
|
||
// 工具表
|
||
// ============================================
|
||
export const tools = pgTable('tools', {
|
||
id: serial('id').primaryKey(),
|
||
// 工具唯一标识
|
||
toolId: varchar('tool_id', { length: 64 }).notNull().unique(),
|
||
// 工具信息
|
||
name: varchar('name', { length: 64 }).notNull(),
|
||
displayName: varchar('display_name', { length: 128 }).notNull(),
|
||
description: text('description'),
|
||
icon: varchar('icon', { length: 64 }),
|
||
// Claude Tool Schema
|
||
inputSchema: jsonb('input_schema').notNull(),
|
||
// 配置
|
||
isEnabled: boolean('is_enabled').default(true),
|
||
isDefault: boolean('is_default').default(false),
|
||
sortOrder: integer('sort_order').default(0),
|
||
// 时间戳
|
||
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(),
|
||
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow(),
|
||
});
|
||
|
||
// ============================================
|
||
// 模型表
|
||
// ============================================
|
||
export const models = pgTable('models', {
|
||
id: serial('id').primaryKey(),
|
||
// 模型唯一标识
|
||
modelId: varchar('model_id', { length: 128 }).notNull().unique(),
|
||
// 模型信息
|
||
name: varchar('name', { length: 64 }).notNull(),
|
||
displayName: varchar('display_name', { length: 128 }).notNull(),
|
||
description: text('description'),
|
||
// 模型类型:claude | codex
|
||
modelType: varchar('model_type', { length: 20 }).notNull().default('claude'),
|
||
// 模型特性
|
||
supportsTools: boolean('supports_tools').default(true),
|
||
supportsThinking: boolean('supports_thinking').default(true),
|
||
supportsVision: boolean('supports_vision').default(false),
|
||
maxTokens: integer('max_tokens').default(8192),
|
||
contextWindow: integer('context_window').default(200000),
|
||
// 配置
|
||
isEnabled: boolean('is_enabled').default(true),
|
||
isDefault: boolean('is_default').default(false),
|
||
sortOrder: integer('sort_order').default(0),
|
||
// 时间戳
|
||
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(),
|
||
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow(),
|
||
});
|
||
|
||
// ============================================
|
||
// 关系定义
|
||
// ============================================
|
||
export const usersRelations = relations(users, ({ many, one }) => ({
|
||
conversations: many(conversations),
|
||
settings: one(userSettings, {
|
||
fields: [users.userId],
|
||
references: [userSettings.userId],
|
||
}),
|
||
}));
|
||
|
||
export const userSettingsRelations = relations(userSettings, ({ one }) => ({
|
||
user: one(users, {
|
||
fields: [userSettings.userId],
|
||
references: [users.userId],
|
||
}),
|
||
}));
|
||
|
||
export const conversationsRelations = relations(conversations, ({ many, one }) => ({
|
||
messages: many(messages),
|
||
user: one(users, {
|
||
fields: [conversations.userId],
|
||
references: [users.userId],
|
||
}),
|
||
}));
|
||
|
||
export const messagesRelations = relations(messages, ({ one }) => ({
|
||
conversation: one(conversations, {
|
||
fields: [messages.conversationId],
|
||
references: [conversations.conversationId],
|
||
}),
|
||
}));
|
||
|
||
// ============================================
|
||
// 类型定义
|
||
// ============================================
|
||
export interface ToolCall {
|
||
id: string;
|
||
name: string;
|
||
input: Record<string, unknown>;
|
||
}
|
||
|
||
export interface ToolResult {
|
||
toolUseId: string;
|
||
content: string;
|
||
isError?: boolean;
|
||
}
|
||
|
||
// 上传的文档数据(用于持久化存储)
|
||
export interface UploadedDocumentData {
|
||
name: string;
|
||
size: number;
|
||
type: string;
|
||
content: string;
|
||
}
|
||
|
||
// 导出类型
|
||
export type User = typeof users.$inferSelect;
|
||
export type NewUser = typeof users.$inferInsert;
|
||
|
||
export type VerificationCode = typeof verificationCodes.$inferSelect;
|
||
export type NewVerificationCode = typeof verificationCodes.$inferInsert;
|
||
|
||
export type UserSettings = typeof userSettings.$inferSelect;
|
||
export type NewUserSettings = typeof userSettings.$inferInsert;
|
||
|
||
export type Conversation = typeof conversations.$inferSelect;
|
||
export type NewConversation = typeof conversations.$inferInsert;
|
||
|
||
export type Message = typeof messages.$inferSelect;
|
||
export type NewMessage = typeof messages.$inferInsert;
|
||
|
||
export type Tool = typeof tools.$inferSelect;
|
||
export type NewTool = typeof tools.$inferInsert;
|
||
|
||
export type Model = typeof models.$inferSelect;
|
||
export type NewModel = typeof models.$inferInsert;
|