771 lines
21 KiB
Markdown
771 lines
21 KiB
Markdown
# 数据库设计文档
|
||
|
||
> **版本:** 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一次
|
||
|