feat(backend): Day 5 - backend basic architecture setup completed

This commit is contained in:
AI Clinical Dev Team
2025-10-10 15:56:45 +08:00
parent 1fac0b5cbf
commit 39e14cfb97
14 changed files with 3616 additions and 26 deletions

View File

@@ -0,0 +1,203 @@
-- CreateTable
CREATE TABLE "users" (
"id" TEXT NOT NULL,
"email" TEXT NOT NULL,
"password" TEXT NOT NULL,
"name" TEXT,
"avatar_url" TEXT,
"role" TEXT NOT NULL DEFAULT 'user',
"status" TEXT NOT NULL DEFAULT 'active',
"kb_quota" INTEGER NOT NULL DEFAULT 3,
"kb_used" INTEGER NOT NULL DEFAULT 0,
"trial_ends_at" TIMESTAMP(3),
"is_trial" BOOLEAN NOT NULL DEFAULT true,
"last_login_at" TIMESTAMP(3),
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "projects" (
"id" TEXT NOT NULL,
"user_id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT NOT NULL,
"conversation_count" INTEGER NOT NULL DEFAULT 0,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "projects_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "conversations" (
"id" TEXT NOT NULL,
"user_id" TEXT NOT NULL,
"project_id" TEXT,
"agent_id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"model_name" TEXT NOT NULL DEFAULT 'deepseek-v3',
"message_count" INTEGER NOT NULL DEFAULT 0,
"total_tokens" INTEGER NOT NULL DEFAULT 0,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "conversations_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "messages" (
"id" TEXT NOT NULL,
"conversation_id" TEXT NOT NULL,
"role" TEXT NOT NULL,
"content" TEXT NOT NULL,
"metadata" JSONB,
"tokens" INTEGER,
"is_pinned" BOOLEAN NOT NULL DEFAULT false,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "messages_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "knowledge_bases" (
"id" TEXT NOT NULL,
"user_id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT,
"dify_dataset_id" TEXT NOT NULL,
"file_count" INTEGER NOT NULL DEFAULT 0,
"total_size_bytes" BIGINT NOT NULL DEFAULT 0,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "knowledge_bases_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "documents" (
"id" TEXT NOT NULL,
"kb_id" TEXT NOT NULL,
"user_id" TEXT NOT NULL,
"filename" TEXT NOT NULL,
"file_type" TEXT NOT NULL,
"file_size_bytes" BIGINT NOT NULL,
"file_url" TEXT NOT NULL,
"dify_document_id" TEXT NOT NULL,
"status" TEXT NOT NULL DEFAULT 'uploading',
"progress" INTEGER NOT NULL DEFAULT 0,
"error_message" TEXT,
"segments_count" INTEGER,
"tokens_count" INTEGER,
"uploaded_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"processed_at" TIMESTAMP(3),
CONSTRAINT "documents_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "admin_logs" (
"id" SERIAL NOT NULL,
"admin_id" TEXT NOT NULL,
"action" TEXT NOT NULL,
"resource_type" TEXT,
"resource_id" TEXT,
"details" JSONB,
"ip_address" TEXT,
"user_agent" TEXT,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "admin_logs_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "users_email_key" ON "users"("email");
-- CreateIndex
CREATE INDEX "users_email_idx" ON "users"("email");
-- CreateIndex
CREATE INDEX "users_status_idx" ON "users"("status");
-- CreateIndex
CREATE INDEX "users_created_at_idx" ON "users"("created_at");
-- CreateIndex
CREATE INDEX "projects_user_id_idx" ON "projects"("user_id");
-- CreateIndex
CREATE INDEX "projects_created_at_idx" ON "projects"("created_at");
-- CreateIndex
CREATE INDEX "conversations_user_id_idx" ON "conversations"("user_id");
-- CreateIndex
CREATE INDEX "conversations_project_id_idx" ON "conversations"("project_id");
-- CreateIndex
CREATE INDEX "conversations_agent_id_idx" ON "conversations"("agent_id");
-- CreateIndex
CREATE INDEX "conversations_created_at_idx" ON "conversations"("created_at");
-- CreateIndex
CREATE INDEX "messages_conversation_id_idx" ON "messages"("conversation_id");
-- CreateIndex
CREATE INDEX "messages_created_at_idx" ON "messages"("created_at");
-- CreateIndex
CREATE INDEX "messages_is_pinned_idx" ON "messages"("is_pinned");
-- CreateIndex
CREATE INDEX "knowledge_bases_user_id_idx" ON "knowledge_bases"("user_id");
-- CreateIndex
CREATE INDEX "knowledge_bases_dify_dataset_id_idx" ON "knowledge_bases"("dify_dataset_id");
-- CreateIndex
CREATE INDEX "documents_kb_id_idx" ON "documents"("kb_id");
-- CreateIndex
CREATE INDEX "documents_user_id_idx" ON "documents"("user_id");
-- CreateIndex
CREATE INDEX "documents_status_idx" ON "documents"("status");
-- CreateIndex
CREATE INDEX "documents_dify_document_id_idx" ON "documents"("dify_document_id");
-- CreateIndex
CREATE INDEX "admin_logs_admin_id_idx" ON "admin_logs"("admin_id");
-- CreateIndex
CREATE INDEX "admin_logs_created_at_idx" ON "admin_logs"("created_at");
-- CreateIndex
CREATE INDEX "admin_logs_action_idx" ON "admin_logs"("action");
-- AddForeignKey
ALTER TABLE "projects" ADD CONSTRAINT "projects_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "conversations" ADD CONSTRAINT "conversations_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "conversations" ADD CONSTRAINT "conversations_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "projects"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "messages" ADD CONSTRAINT "messages_conversation_id_fkey" FOREIGN KEY ("conversation_id") REFERENCES "conversations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "knowledge_bases" ADD CONSTRAINT "knowledge_bases_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "documents" ADD CONSTRAINT "documents_kb_id_fkey" FOREIGN KEY ("kb_id") REFERENCES "knowledge_bases"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "documents" ADD CONSTRAINT "documents_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "admin_logs" ADD CONSTRAINT "admin_logs_admin_id_fkey" FOREIGN KEY ("admin_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
provider = "postgresql"

View File

@@ -0,0 +1,182 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
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")
}