chore: project initialization - Day 4 environment setup
This commit is contained in:
770
docs/01-设计文档/数据库设计文档.md
Normal file
770
docs/01-设计文档/数据库设计文档.md
Normal file
@@ -0,0 +1,770 @@
|
||||
# 数据库设计文档
|
||||
|
||||
> **版本:** v1.0
|
||||
> **创建日期:** 2025-10-10
|
||||
> **数据库:** PostgreSQL 15+
|
||||
> **ORM:** Prisma
|
||||
|
||||
---
|
||||
|
||||
## 📋 目录
|
||||
|
||||
1. [数据库概述](#数据库概述)
|
||||
2. [ER图](#er图)
|
||||
3. [表结构设计](#表结构设计)
|
||||
4. [索引设计](#索引设计)
|
||||
5. [数据约束](#数据约束)
|
||||
6. [数据迁移策略](#数据迁移策略)
|
||||
|
||||
---
|
||||
|
||||
## 数据库概述
|
||||
|
||||
### 设计原则
|
||||
- ✅ 遵循第三范式(3NF)
|
||||
- ✅ 使用UUID作为主键
|
||||
- ✅ 所有表包含created_at和updated_at时间戳
|
||||
- ✅ 使用软删除(保留deleted_at字段,重要表)
|
||||
- ✅ 外键约束使用CASCADE删除
|
||||
- ✅ 敏感字段加密存储(密码使用bcrypt)
|
||||
|
||||
### 命名规范
|
||||
- 表名:复数形式,下划线分隔(如:`users`、`knowledge_bases`)
|
||||
- 字段名:下划线分隔(如:`created_at`、`user_id`)
|
||||
- 索引名:`idx_表名_字段名`(如:`idx_users_email`)
|
||||
- 外键名:`fk_表名_关联表名`(如:`fk_projects_users`)
|
||||
|
||||
---
|
||||
|
||||
## ER图
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ Users │
|
||||
└──────┬──────┘
|
||||
│
|
||||
│ 1:N
|
||||
├─────────────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
┌─────────────┐ ┌──────────────┐
|
||||
│ Projects │ │KnowledgeBases│
|
||||
└──────┬──────┘ └──────┬───────┘
|
||||
│ │
|
||||
│ 1:N │ 1:N
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│Conversations │ │ Documents │
|
||||
└──────┬───────┘ └──────────────┘
|
||||
│
|
||||
│ 1:N
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ Messages │
|
||||
└──────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 表结构设计
|
||||
|
||||
### 1. users - 用户表
|
||||
|
||||
**用途:** 存储用户基本信息和认证信息
|
||||
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
id VARCHAR(50) PRIMARY KEY DEFAULT gen_random_uuid()::VARCHAR,
|
||||
email VARCHAR(255) NOT NULL UNIQUE,
|
||||
password VARCHAR(255) NOT NULL, -- bcrypt加密
|
||||
name VARCHAR(100),
|
||||
avatar_url TEXT,
|
||||
|
||||
-- 角色和状态
|
||||
role VARCHAR(20) NOT NULL DEFAULT 'user', -- user, admin
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active, inactive, suspended
|
||||
|
||||
-- 配额(知识库限制)
|
||||
kb_quota INTEGER DEFAULT 3,
|
||||
kb_used INTEGER DEFAULT 0,
|
||||
|
||||
-- 试用信息
|
||||
trial_ends_at TIMESTAMP,
|
||||
is_trial BOOLEAN DEFAULT true,
|
||||
|
||||
-- 时间戳
|
||||
last_login_at TIMESTAMP,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW(),
|
||||
|
||||
-- 索引
|
||||
CONSTRAINT check_email_format CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}$'),
|
||||
CONSTRAINT check_kb_quota CHECK (kb_used <= kb_quota)
|
||||
);
|
||||
|
||||
-- 索引
|
||||
CREATE INDEX idx_users_email ON users(email);
|
||||
CREATE INDEX idx_users_status ON users(status);
|
||||
CREATE INDEX idx_users_created_at ON users(created_at);
|
||||
|
||||
-- 注释
|
||||
COMMENT ON TABLE users IS '用户表';
|
||||
COMMENT ON COLUMN users.role IS '用户角色:user-普通用户, admin-管理员';
|
||||
COMMENT ON COLUMN users.status IS '账户状态:active-激活, inactive-未激活, suspended-暂停';
|
||||
```
|
||||
|
||||
**字段说明:**
|
||||
| 字段 | 类型 | 说明 | 必填 | 默认值 |
|
||||
|------|------|------|------|--------|
|
||||
| id | VARCHAR(50) | 用户ID(UUID) | ✅ | 自动生成 |
|
||||
| email | VARCHAR(255) | 邮箱(登录名) | ✅ | - |
|
||||
| password | VARCHAR(255) | 密码(bcrypt) | ✅ | - |
|
||||
| name | VARCHAR(100) | 用户姓名 | ❌ | NULL |
|
||||
| role | VARCHAR(20) | 角色 | ✅ | 'user' |
|
||||
| status | VARCHAR(20) | 状态 | ✅ | 'active' |
|
||||
| kb_quota | INTEGER | 知识库配额 | ✅ | 3 |
|
||||
| kb_used | INTEGER | 已使用知识库数 | ✅ | 0 |
|
||||
|
||||
---
|
||||
|
||||
### 2. projects - 项目/课题表
|
||||
|
||||
**用途:** 存储用户创建的研究项目/课题
|
||||
|
||||
```sql
|
||||
CREATE TABLE projects (
|
||||
id VARCHAR(50) PRIMARY KEY DEFAULT gen_random_uuid()::VARCHAR,
|
||||
user_id VARCHAR(50) NOT NULL,
|
||||
|
||||
-- 项目信息
|
||||
name VARCHAR(200) NOT NULL,
|
||||
description TEXT NOT NULL, -- 项目背景信息(重要!用于上下文注入)
|
||||
|
||||
-- 统计信息
|
||||
conversation_count INTEGER DEFAULT 0,
|
||||
|
||||
-- 时间戳
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW(),
|
||||
|
||||
-- 外键
|
||||
CONSTRAINT fk_projects_users FOREIGN KEY (user_id)
|
||||
REFERENCES users(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- 索引
|
||||
CREATE INDEX idx_projects_user_id ON projects(user_id);
|
||||
CREATE INDEX idx_projects_created_at ON projects(created_at);
|
||||
|
||||
-- 注释
|
||||
COMMENT ON TABLE projects IS '项目/课题表';
|
||||
COMMENT ON COLUMN projects.description IS '项目背景信息,会自动注入到对话上下文中';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. conversations - 对话表
|
||||
|
||||
**用途:** 存储用户与智能体的对话会话
|
||||
|
||||
```sql
|
||||
CREATE TABLE conversations (
|
||||
id VARCHAR(50) PRIMARY KEY DEFAULT gen_random_uuid()::VARCHAR,
|
||||
user_id VARCHAR(50) NOT NULL,
|
||||
project_id VARCHAR(50), -- 可选,全局快速问答时为NULL
|
||||
agent_id VARCHAR(50) NOT NULL, -- 智能体ID(对应config/agents.yaml)
|
||||
|
||||
-- 对话信息
|
||||
title VARCHAR(200) NOT NULL,
|
||||
model_name VARCHAR(50) DEFAULT 'deepseek-v3',
|
||||
|
||||
-- 统计信息
|
||||
message_count INTEGER DEFAULT 0,
|
||||
total_tokens INTEGER DEFAULT 0,
|
||||
|
||||
-- 时间戳
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW(),
|
||||
|
||||
-- 外键
|
||||
CONSTRAINT fk_conversations_users FOREIGN KEY (user_id)
|
||||
REFERENCES users(id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_conversations_projects FOREIGN KEY (project_id)
|
||||
REFERENCES projects(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- 索引
|
||||
CREATE INDEX idx_conversations_user_id ON conversations(user_id);
|
||||
CREATE INDEX idx_conversations_project_id ON conversations(project_id);
|
||||
CREATE INDEX idx_conversations_agent_id ON conversations(agent_id);
|
||||
CREATE INDEX idx_conversations_created_at ON conversations(created_at);
|
||||
|
||||
-- 注释
|
||||
COMMENT ON TABLE conversations IS '对话会话表';
|
||||
COMMENT ON COLUMN conversations.project_id IS '项目ID,全局快速问答时为NULL';
|
||||
COMMENT ON COLUMN conversations.agent_id IS '智能体ID,对应配置文件中的智能体';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. messages - 消息表
|
||||
|
||||
**用途:** 存储对话中的每条消息
|
||||
|
||||
```sql
|
||||
CREATE TABLE messages (
|
||||
id VARCHAR(50) PRIMARY KEY DEFAULT gen_random_uuid()::VARCHAR,
|
||||
conversation_id VARCHAR(50) NOT NULL,
|
||||
|
||||
-- 消息内容
|
||||
role VARCHAR(20) NOT NULL, -- user, assistant
|
||||
content TEXT NOT NULL,
|
||||
|
||||
-- 元数据(可选)
|
||||
metadata JSONB, -- 存储额外信息,如引用的知识库、模型参数等
|
||||
|
||||
-- 统计信息
|
||||
tokens INTEGER,
|
||||
|
||||
-- 是否固定到项目背景
|
||||
is_pinned BOOLEAN DEFAULT false,
|
||||
|
||||
-- 时间戳
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
|
||||
-- 外键
|
||||
CONSTRAINT fk_messages_conversations FOREIGN KEY (conversation_id)
|
||||
REFERENCES conversations(id) ON DELETE CASCADE,
|
||||
|
||||
-- 约束
|
||||
CONSTRAINT check_role CHECK (role IN ('user', 'assistant'))
|
||||
);
|
||||
|
||||
-- 索引
|
||||
CREATE INDEX idx_messages_conversation_id ON messages(conversation_id);
|
||||
CREATE INDEX idx_messages_created_at ON messages(created_at);
|
||||
CREATE INDEX idx_messages_is_pinned ON messages(is_pinned);
|
||||
|
||||
-- 注释
|
||||
COMMENT ON TABLE messages IS '对话消息表';
|
||||
COMMENT ON COLUMN messages.is_pinned IS '是否固定到项目背景,用于动态更新项目描述';
|
||||
COMMENT ON COLUMN messages.metadata IS 'JSON格式,存储引用的知识库、使用的模型等';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. knowledge_bases - 知识库表
|
||||
|
||||
**用途:** 存储用户创建的个人知识库
|
||||
|
||||
```sql
|
||||
CREATE TABLE knowledge_bases (
|
||||
id VARCHAR(50) PRIMARY KEY DEFAULT gen_random_uuid()::VARCHAR,
|
||||
user_id VARCHAR(50) NOT NULL,
|
||||
|
||||
-- 知识库信息
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
|
||||
-- Dify集成
|
||||
dify_dataset_id VARCHAR(100) NOT NULL, -- Dify中的知识库ID
|
||||
|
||||
-- 统计信息
|
||||
file_count INTEGER DEFAULT 0,
|
||||
total_size_bytes BIGINT DEFAULT 0,
|
||||
|
||||
-- 时间戳
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW(),
|
||||
|
||||
-- 外键
|
||||
CONSTRAINT fk_kb_users FOREIGN KEY (user_id)
|
||||
REFERENCES users(id) ON DELETE CASCADE,
|
||||
|
||||
-- 约束:每个用户最多3个知识库
|
||||
CONSTRAINT check_kb_limit CHECK (
|
||||
(SELECT COUNT(*) FROM knowledge_bases WHERE user_id = knowledge_bases.user_id) <= 3
|
||||
)
|
||||
);
|
||||
|
||||
-- 索引
|
||||
CREATE INDEX idx_kb_user_id ON knowledge_bases(user_id);
|
||||
CREATE INDEX idx_kb_dify_dataset_id ON knowledge_bases(dify_dataset_id);
|
||||
|
||||
-- 注释
|
||||
COMMENT ON TABLE knowledge_bases IS '知识库表,每个用户最多3个';
|
||||
COMMENT ON COLUMN knowledge_bases.dify_dataset_id IS 'Dify中对应的数据集ID';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. documents - 文档表
|
||||
|
||||
**用途:** 存储知识库中上传的文档
|
||||
|
||||
```sql
|
||||
CREATE TABLE documents (
|
||||
id VARCHAR(50) PRIMARY KEY DEFAULT gen_random_uuid()::VARCHAR,
|
||||
kb_id VARCHAR(50) NOT NULL,
|
||||
user_id VARCHAR(50) NOT NULL,
|
||||
|
||||
-- 文件信息
|
||||
filename VARCHAR(255) NOT NULL,
|
||||
file_type VARCHAR(20) NOT NULL, -- pdf, docx
|
||||
file_size_bytes BIGINT NOT NULL,
|
||||
file_url TEXT NOT NULL, -- 对象存储URL
|
||||
|
||||
-- Dify集成
|
||||
dify_document_id VARCHAR(100) NOT NULL, -- Dify中的文档ID
|
||||
|
||||
-- 处理状态
|
||||
status VARCHAR(20) DEFAULT 'uploading', -- uploading, processing, completed, failed
|
||||
progress INTEGER DEFAULT 0, -- 0-100
|
||||
error_message TEXT,
|
||||
|
||||
-- 处理结果
|
||||
segments_count INTEGER, -- 切分的段落数
|
||||
tokens_count INTEGER, -- token数量
|
||||
|
||||
-- 时间戳
|
||||
uploaded_at TIMESTAMP DEFAULT NOW(),
|
||||
processed_at TIMESTAMP,
|
||||
|
||||
-- 外键
|
||||
CONSTRAINT fk_documents_kb FOREIGN KEY (kb_id)
|
||||
REFERENCES knowledge_bases(id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_documents_users FOREIGN KEY (user_id)
|
||||
REFERENCES users(id) ON DELETE CASCADE,
|
||||
|
||||
-- 约束:每个知识库最多50个文档
|
||||
CONSTRAINT check_doc_limit CHECK (
|
||||
(SELECT COUNT(*) FROM documents WHERE kb_id = documents.kb_id) <= 50
|
||||
),
|
||||
|
||||
-- 约束:状态和进度
|
||||
CONSTRAINT check_status CHECK (status IN ('uploading', 'processing', 'completed', 'failed')),
|
||||
CONSTRAINT check_progress CHECK (progress >= 0 AND progress <= 100)
|
||||
);
|
||||
|
||||
-- 索引
|
||||
CREATE INDEX idx_documents_kb_id ON documents(kb_id);
|
||||
CREATE INDEX idx_documents_user_id ON documents(user_id);
|
||||
CREATE INDEX idx_documents_status ON documents(status);
|
||||
CREATE INDEX idx_documents_dify_document_id ON documents(dify_document_id);
|
||||
|
||||
-- 注释
|
||||
COMMENT ON TABLE documents IS '文档表,每个知识库最多50个文档';
|
||||
COMMENT ON COLUMN documents.status IS '处理状态:uploading-上传中, processing-处理中, completed-完成, failed-失败';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7. admin_logs - 管理员操作日志表(可选)
|
||||
|
||||
**用途:** 记录管理员的敏感操作
|
||||
|
||||
```sql
|
||||
CREATE TABLE admin_logs (
|
||||
id SERIAL PRIMARY KEY,
|
||||
admin_id VARCHAR(50) NOT NULL,
|
||||
|
||||
-- 操作信息
|
||||
action VARCHAR(100) NOT NULL, -- 操作类型
|
||||
resource_type VARCHAR(50), -- 资源类型:user, conversation, etc.
|
||||
resource_id VARCHAR(50), -- 资源ID
|
||||
|
||||
-- 详细信息
|
||||
details JSONB,
|
||||
ip_address VARCHAR(45),
|
||||
user_agent TEXT,
|
||||
|
||||
-- 时间戳
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
|
||||
-- 外键
|
||||
CONSTRAINT fk_admin_logs_users FOREIGN KEY (admin_id)
|
||||
REFERENCES users(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- 索引
|
||||
CREATE INDEX idx_admin_logs_admin_id ON admin_logs(admin_id);
|
||||
CREATE INDEX idx_admin_logs_created_at ON admin_logs(created_at);
|
||||
CREATE INDEX idx_admin_logs_action ON admin_logs(action);
|
||||
|
||||
-- 注释
|
||||
COMMENT ON TABLE admin_logs IS '管理员操作日志表,用于审计';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 索引设计
|
||||
|
||||
### 主要索引
|
||||
|
||||
| 表名 | 索引字段 | 类型 | 用途 |
|
||||
|------|---------|------|------|
|
||||
| users | email | UNIQUE | 登录查询 |
|
||||
| users | status | INDEX | 按状态筛选用户 |
|
||||
| projects | user_id | INDEX | 查询用户的项目 |
|
||||
| conversations | user_id | INDEX | 查询用户的对话 |
|
||||
| conversations | project_id | INDEX | 查询项目的对话 |
|
||||
| conversations | agent_id | INDEX | 统计智能体使用情况 |
|
||||
| messages | conversation_id | INDEX | 查询对话消息 |
|
||||
| knowledge_bases | user_id | INDEX | 查询用户的知识库 |
|
||||
| documents | kb_id | INDEX | 查询知识库的文档 |
|
||||
| documents | status | INDEX | 筛选处理状态 |
|
||||
|
||||
### 复合索引(如需优化性能可添加)
|
||||
|
||||
```sql
|
||||
-- 按时间范围查询用户的对话
|
||||
CREATE INDEX idx_conversations_user_created
|
||||
ON conversations(user_id, created_at DESC);
|
||||
|
||||
-- 按项目查询特定智能体的对话
|
||||
CREATE INDEX idx_conversations_project_agent
|
||||
ON conversations(project_id, agent_id);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 数据约束
|
||||
|
||||
### 业务约束
|
||||
|
||||
1. **知识库数量限制**
|
||||
- 每个用户最多3个知识库
|
||||
- 通过CHECK约束和应用层双重控制
|
||||
|
||||
2. **文档数量限制**
|
||||
- 每个知识库最多50个文档
|
||||
- 通过CHECK约束和应用层双重控制
|
||||
|
||||
3. **邮箱格式验证**
|
||||
- 使用正则表达式验证邮箱格式
|
||||
|
||||
4. **密码安全**
|
||||
- 使用bcrypt加密,成本因子12
|
||||
- 密码长度至少8位(应用层验证)
|
||||
|
||||
### 数据完整性
|
||||
|
||||
1. **级联删除**
|
||||
- 删除用户 → 级联删除其项目、对话、知识库
|
||||
- 删除项目 → 级联删除其对话
|
||||
- 删除知识库 → 级联删除其文档
|
||||
|
||||
2. **外键约束**
|
||||
- 所有外键都设置了ON DELETE CASCADE
|
||||
- 保证数据一致性
|
||||
|
||||
---
|
||||
|
||||
## 数据迁移策略
|
||||
|
||||
### 初始化迁移
|
||||
|
||||
```bash
|
||||
# 创建初始迁移
|
||||
npx prisma migrate dev --name init
|
||||
|
||||
# 生成Prisma Client
|
||||
npx prisma generate
|
||||
```
|
||||
|
||||
### 迁移命名规范
|
||||
|
||||
```
|
||||
yyyymmdd_描述.sql
|
||||
例如:
|
||||
20251010_init.sql
|
||||
20251015_add_admin_logs.sql
|
||||
20251020_add_user_quotas.sql
|
||||
```
|
||||
|
||||
### 生产环境迁移流程
|
||||
|
||||
1. 在开发环境测试迁移
|
||||
2. 备份生产数据库
|
||||
3. 执行迁移脚本
|
||||
4. 验证数据完整性
|
||||
5. 回滚计划(如有问题)
|
||||
|
||||
---
|
||||
|
||||
## Prisma Schema
|
||||
|
||||
### 完整的schema.prisma
|
||||
|
||||
```prisma
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(uuid())
|
||||
email String @unique
|
||||
password String
|
||||
name String?
|
||||
avatarUrl String? @map("avatar_url")
|
||||
|
||||
role String @default("user")
|
||||
status String @default("active")
|
||||
|
||||
kbQuota Int @default(3) @map("kb_quota")
|
||||
kbUsed Int @default(0) @map("kb_used")
|
||||
|
||||
trialEndsAt DateTime? @map("trial_ends_at")
|
||||
isTrial Boolean @default(true) @map("is_trial")
|
||||
|
||||
lastLoginAt DateTime? @map("last_login_at")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
projects Project[]
|
||||
conversations Conversation[]
|
||||
knowledgeBases KnowledgeBase[]
|
||||
documents Document[]
|
||||
adminLogs AdminLog[]
|
||||
|
||||
@@index([email])
|
||||
@@index([status])
|
||||
@@index([createdAt])
|
||||
@@map("users")
|
||||
}
|
||||
|
||||
model Project {
|
||||
id String @id @default(uuid())
|
||||
userId String @map("user_id")
|
||||
name String
|
||||
description String @db.Text
|
||||
conversationCount Int @default(0) @map("conversation_count")
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
conversations Conversation[]
|
||||
|
||||
@@index([userId])
|
||||
@@index([createdAt])
|
||||
@@map("projects")
|
||||
}
|
||||
|
||||
model Conversation {
|
||||
id String @id @default(uuid())
|
||||
userId String @map("user_id")
|
||||
projectId String? @map("project_id")
|
||||
agentId String @map("agent_id")
|
||||
title String
|
||||
modelName String @default("deepseek-v3") @map("model_name")
|
||||
messageCount Int @default(0) @map("message_count")
|
||||
totalTokens Int @default(0) @map("total_tokens")
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
messages Message[]
|
||||
|
||||
@@index([userId])
|
||||
@@index([projectId])
|
||||
@@index([agentId])
|
||||
@@index([createdAt])
|
||||
@@map("conversations")
|
||||
}
|
||||
|
||||
model Message {
|
||||
id String @id @default(uuid())
|
||||
conversationId String @map("conversation_id")
|
||||
role String
|
||||
content String @db.Text
|
||||
metadata Json?
|
||||
tokens Int?
|
||||
isPinned Boolean @default(false) @map("is_pinned")
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([conversationId])
|
||||
@@index([createdAt])
|
||||
@@index([isPinned])
|
||||
@@map("messages")
|
||||
}
|
||||
|
||||
model KnowledgeBase {
|
||||
id String @id @default(uuid())
|
||||
userId String @map("user_id")
|
||||
name String
|
||||
description String?
|
||||
difyDatasetId String @map("dify_dataset_id")
|
||||
fileCount Int @default(0) @map("file_count")
|
||||
totalSizeBytes BigInt @default(0) @map("total_size_bytes")
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
documents Document[]
|
||||
|
||||
@@index([userId])
|
||||
@@index([difyDatasetId])
|
||||
@@map("knowledge_bases")
|
||||
}
|
||||
|
||||
model Document {
|
||||
id String @id @default(uuid())
|
||||
kbId String @map("kb_id")
|
||||
userId String @map("user_id")
|
||||
filename String
|
||||
fileType String @map("file_type")
|
||||
fileSizeBytes BigInt @map("file_size_bytes")
|
||||
fileUrl String @map("file_url")
|
||||
difyDocumentId String @map("dify_document_id")
|
||||
status String @default("uploading")
|
||||
progress Int @default(0)
|
||||
errorMessage String? @map("error_message")
|
||||
segmentsCount Int? @map("segments_count")
|
||||
tokensCount Int? @map("tokens_count")
|
||||
|
||||
uploadedAt DateTime @default(now()) @map("uploaded_at")
|
||||
processedAt DateTime? @map("processed_at")
|
||||
|
||||
knowledgeBase KnowledgeBase @relation(fields: [kbId], references: [id], onDelete: Cascade)
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([kbId])
|
||||
@@index([userId])
|
||||
@@index([status])
|
||||
@@index([difyDocumentId])
|
||||
@@map("documents")
|
||||
}
|
||||
|
||||
model AdminLog {
|
||||
id Int @id @default(autoincrement())
|
||||
adminId String @map("admin_id")
|
||||
action String
|
||||
resourceType String? @map("resource_type")
|
||||
resourceId String? @map("resource_id")
|
||||
details Json?
|
||||
ipAddress String? @map("ip_address")
|
||||
userAgent String? @map("user_agent")
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
admin User @relation(fields: [adminId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([adminId])
|
||||
@@index([createdAt])
|
||||
@@index([action])
|
||||
@@map("admin_logs")
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 数据字典
|
||||
|
||||
### 枚举值定义
|
||||
|
||||
**用户角色 (user.role)**
|
||||
- `user` - 普通用户
|
||||
- `admin` - 管理员
|
||||
|
||||
**账户状态 (user.status)**
|
||||
- `active` - 激活(正常使用)
|
||||
- `inactive` - 未激活(注册但未验证邮箱)
|
||||
- `suspended` - 暂停(违规或欠费)
|
||||
|
||||
**消息角色 (message.role)**
|
||||
- `user` - 用户消息
|
||||
- `assistant` - AI助手消息
|
||||
|
||||
**文档状态 (document.status)**
|
||||
- `uploading` - 上传中
|
||||
- `processing` - Dify处理中
|
||||
- `completed` - 处理完成
|
||||
- `failed` - 处理失败
|
||||
|
||||
**文件类型 (document.file_type)**
|
||||
- `pdf` - PDF文档
|
||||
- `docx` - Word文档
|
||||
|
||||
---
|
||||
|
||||
## 数据安全
|
||||
|
||||
### 敏感数据处理
|
||||
|
||||
1. **密码**
|
||||
- 使用bcrypt加密(成本因子12)
|
||||
- 不可逆加密,无法还原明文
|
||||
|
||||
2. **API Keys**
|
||||
- 不存储在数据库中
|
||||
- 使用环境变量管理
|
||||
|
||||
3. **用户数据隔离**
|
||||
- 所有查询必须包含user_id过滤
|
||||
- 防止越权访问
|
||||
|
||||
### 备份策略
|
||||
|
||||
1. **自动备份**
|
||||
- 每日全量备份
|
||||
- 保留最近7天
|
||||
|
||||
2. **手动备份**
|
||||
- 重大升级前手动备份
|
||||
- 保留3个版本
|
||||
|
||||
---
|
||||
|
||||
## 性能优化建议
|
||||
|
||||
### 查询优化
|
||||
|
||||
1. **分页查询**
|
||||
- 使用 LIMIT + OFFSET
|
||||
- 或使用游标分页(基于ID)
|
||||
|
||||
2. **避免N+1查询**
|
||||
- 使用Prisma的include和select
|
||||
- 预加载关联数据
|
||||
|
||||
3. **合理使用索引**
|
||||
- 频繁查询的字段建立索引
|
||||
- 定期检查索引使用情况
|
||||
|
||||
### 数据归档
|
||||
|
||||
1. **历史数据归档**
|
||||
- 对话超过6个月自动归档
|
||||
- 归档数据移至单独的表
|
||||
|
||||
2. **日志清理**
|
||||
- admin_logs保留3个月
|
||||
- 定期清理过期日志
|
||||
|
||||
---
|
||||
|
||||
## 版本变更记录
|
||||
|
||||
| 版本 | 日期 | 变更内容 | 作者 |
|
||||
|------|------|---------|------|
|
||||
| v1.0 | 2025-10-10 | 初始版本,定义所有核心表 | 开发团队 |
|
||||
|
||||
---
|
||||
|
||||
**文档维护:** 数据库结构变更需同步更新本文档
|
||||
**Review频率:** 每个里程碑结束后Review一次
|
||||
|
||||
Reference in New Issue
Block a user