迷新白的博客 迷新白的博客
首页
随笔
  • Vuepress
  • Springboot
  • 开发工具
  • 系统工具
读吧
  • 智能浇花系统 (opens new window)
  • 用户中心系统 (opens new window)
  • 关于
  • 友情链接
GitHub (opens new window)

迷新白

愿你平安
首页
随笔
  • Vuepress
  • Springboot
  • 开发工具
  • 系统工具
读吧
  • 智能浇花系统 (opens new window)
  • 用户中心系统 (opens new window)
  • 关于
  • 友情链接
GitHub (opens new window)
  • 用户中心系统

  • 仿Deepseek官网AI聊天网站

    • 需求分析
    • 首页&聊天页开发
    • 接入deepseek
    • 数据库搭建
      • 注册账号,创建数据库项目
        • 需求分析
      • ORM
        • 数据库初始化
        • 定义创建数据库表方法
        • 聊天模块
        • 内容模块
    • 首页完善(前端)
    • 登陆系统-clerk
    • 首页完善(后端)
    • 保留上下文对话
    • 第一次对话完善
  • 尤克里里音月-Flutter(需求分析阶段)

  • 项目
  • 仿Deepseek官网AI聊天网站
迷新白
2025-04-24
目录

数据库搭建

# 04.数据库搭建

数据库采用supabase

登入supabasehttps://supabase.com/

# 注册账号,创建数据库项目

# 需求分析

数据库需要包含的表

  • 聊天表格chatsTable
    • id用户id使用的大模型
    • userId,用户名
    • title,标题
    • model,使用的大模型
  • 消息表格messagesTable
    • id,用户id
    • chatId,聊天页的编号
    • role,对话的角色,分为AI或者用户
    • content,聊天的内容
  1. 点击注册,可以使用github关联

  2. 进入个人Dashboard 仪表板image-20250424233909509

  3. 创建项目image-20250424234000690 记得将密码保存起来,放在.env文件里

  4. 点击左上角的connect image-20250424234217958


    将链接地址保存在.env文件中image-20250424234146311

# ORM

# 数据库初始化

ORM是指对象关系映射技术(Object Relational Mapping),用于实现面向对象编程语言里不同类型系统的数据之间的转换。

我们使用 drizzle ORM

  • 找到Get Startedimage-20250424234909523

  • 有我们需要的Supabase,打开官方文档 Drizzle ORM - PostgreSQL (opens new window)

image-20250424234938166
  • Step1.根据官方文档安装依赖image-20250425000844604

  • Step2.安装好后,回到我们的.env 将刚才保存的URL和密码按以下格式改好

    DATABASE_URL=postgresql://postgres:[PASSWORD]@db.upvkqcmscrcetlwlfimu.supabase.co:5432/postgres
    
    1
  • Step3.在src/db中创建index.ts文件进行连接的初始化,我们需要同步数据,所以复制官方文档的代码 进行修改

    import { drizzle } from 'drizzle-orm/postgres-js'
    import postgres from 'postgres'
    
        const client = postgres(process.env.DATABASE_URL)
        const db = drizzle({ client });
    
    1
    2
    3
    4
    5
  • Step4.创建数据库表。在db文件夹里创建一个新文件schema.ts

    import { integer, pgTable, varchar } from "drizzle-orm/pg-core";
    
    export const usersTable = pgTable("users", {
      id: integer().primaryKey().generatedAlwaysAsIdentity(),
      name: varchar({ length: 255 }).notNull(),
      age: integer().notNull(),
      email: varchar({ length: 255 }).notNull().unique(),
    });
    
    1
    2
    3
    4
    5
    6
    7
    8

    官方帮助我们定义了一个User表,包含用户id、用户名字、年龄、邮箱

    我们仿照这个格式定义一个chatsTable(聊天表格),包含用户id,名字,标题,使用的大模型

    export const chatsTable = pgTable("chats", {
        id: serial().primaryKey(),
        userId:text("user_id").notNull(),
        title:text("title").notNull(),
        model:text("model").notNull(),
    })
    
    //以上代码后续会出现错误 下面是更改之后的
    export const chatsTable = pgTable("chats", {
        id: serial("id").primaryKey(),
        userId: text("user_id").notNull(),
        title: text("title").notNull(),
        model: text("model").notNull()
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    并且引入好依赖

    import {integer, pgTable, serial, varchar , text} from "drizzle-orm/pg-core";
    
    1

    定义一个(messagesTable)消息表格,包含用户id、聊天页的编号、对话的角色,分为AI或者用户、聊天的内容

export const messagesTable = pgTable("messages", {
    id: serial().primaryKey(),
    chatId:text("chat_id").notNull(),
    role:text("role").notNull(),
    content:text("content").notNull(),
})

//以上代码后续会出现错误 下面是更改之后的
export const messagesTable = pgTable("messages", {
    id: serial("id").primaryKey(),
    chatId: integer("chat_id").references(() => chatsTable.id),
    role: text("role").notNull(),
    content: text("content").notNull()
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14

我们需要再定义两个类型

export type ChatModel =typeof chatsTable.$inferSelect
export type MessageModel =typeof messagesTable.$inferSelect
1
2

利用 Prisma 的类型推断功能,根据数据库表的查询结果自动定义相应的类型。

  • Step5.在根目录(和package.json同级)新增一个配置文件drizzle.config.ts 复制代码,这里记录了schema(概要|描述数据库的组织和结构)的路径、dialect表示输出的方式

    import 'dotenv/config';
    import { defineConfig } from 'drizzle-kit';
    
    export default defineConfig({
      out: './drizzle',
      schema: './src/db/schema.ts',
      dialect: 'postgresql',
      dbCredentials: {
        url: process.env.DATABASE_URL!,
      },
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  • Step6.将User表删除,我们用了Clerk定义,后续把自己写的后端接上的时候再用。

  • Step7.将我们修改过后的代码应用于数据库,在终端执行命令npx drizzle-kit push 测试连接失败❌ ETIMEDOUT表示连接超时image-20250425015329449查了一下资料

    IPv6 地址的格式是 8组4位十六进制数,用冒号分隔

    2406:da18:243:740f:21f6:96ab:3aeb:14fa

    而 IPv4 地址是 *4组1-3位十进制数,用点分隔

    192.168.1.1

    不支持ipv6改用另外两个连接,就是把env文件里的url地址改为这两个image-20250425150047284

    此方法是在项目教学的评论区找到的,建立一个能够交流的社区能够很好的帮助到其他人呀。

  • 连接成功✔image-20250425150241400回到supabase项目中 查看连接image-20250425150232604

发现两个表格的"id"项没有出现预期的关联,原因是建表的语句里少了键值

image-20250425150741543并且chat_id的属性需要从text转化成integer。由于我们改变了数据类型,image-20250425151141123由于我们改变了数据类型, Drizzle-Kit 在迁移过程中无法自动将 chat_id 列从其当前类型,直接回到supabase进行修改,发现还是不行(流汗了) 直接删除表重新创建吧image-20250425151752603

此时成功

image-20250425152342892

# 定义创建数据库表方法

JS的模块语言

  • export 关键字:表示这个函数可以被其他模块导入和使用。这是 TypeScript 和 JavaScript 中模块化编程的一部分。

  • const 关键字:声明一个常量,表示这个函数一旦被定义,其引用不能被重新赋值。

  • async 关键字:表示这个函数是一个异步函数。异步函数允许在函数内部使用 await 关键字,用于等待异步操作(如数据库操作)完成。

  • await 关键字:用于等待异步操作完成。await 只能在 async 函数中使用。

  • 箭头函数语法:=> 表示这是一个箭头函数,箭头函数是 ES6 中引入的一种更简洁的函数写法。箭头函数没有自己的 this 上下文,它会捕获其所在上下文的 this 值。

  • # try...catch 块
    • try 块:包含可能抛出错误的代码。如果代码执行过程中发生错误,错误会被捕获并传递到 catch 块中。
    • catch 块:捕获 try 块中抛出的错误,并进行处理。error 是捕获到的错误对象。

构建创建操作数据库表的函数

chat_id对应多个message,是一对多

# 聊天模块

创建聊天的表格,记录标题,用户id,以及使用的大模型,可以说是内容模块的“身份证“。

插入title,userid,model

// chats
export const createChat = async (title: string, userId: string, model: string) => {
    try {
        const [newChat] = await db.insert(chatsTable).values({
            title,
            userId,
            model
        }).returning()
        return newChat
    } catch (error) {
        console.log("error creating chat", error)
        return null
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

定义一个get方法,从数据库中获取聊天的数据

export const getChat = async (chatId: number, userId: string) => {
    try {
        //用and连接查询
        const chat = await db.select().from(chatsTable).where(and(eq(chatsTable.id, chatId), eq(chatsTable.userId, userId)))
        if (chat.length === 0) {
            return null
        }
        return chat[0]
    } catch (error) {
        console.log("error getting chat", error)
        return null
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

侧边栏获取聊天函数

export const getChats = async (userId: string) => {
    try {
        const chats = await db.select().from(chatsTable).where(eq(chatsTable.userId, userId)).orderBy(desc(chatsTable.id))

        return chats
    } catch (error) {
        console.log("error getting chats", error)
        return null
    }
}
1
2
3
4
5
6
7
8
9
10

# 内容模块

按同样的结构,定义查询消息的方法


// messages
export const createMessage = async (chat_id: number, content: string, role: string) => {
    try {
        const [newMessage] = await db.insert(messagesTable).values({
            content: content,
            chatId: chat_id,
            role: role
        }).returning()
        return newMessage
    } catch (error) {
        console.log("error createMessage", error)
        return null
    }
}

//根据chatid获取消息
export const getMessagesByChatId = async (chatId: number) => {
    try {
        const messages = await db.select().from(messagesTable).where(eq(messagesTable.chatId, chatId))
        return messages
    } catch (error) {
        console.log("error getMessagesByChatId", error)
        return null
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

这样数据库就搭建好了

文字写于:广东

更新时间: 2025/4/25 20:22:48
接入deepseek
首页完善(前端)

← 接入deepseek 首页完善(前端)→

最近更新
01
第一次对话完善
04-27
02
保留上下文对话
04-27
03
首页完善(后端)
04-27
更多文章>
Theme by Vdoing | Copyright © 2022-2025 迷新白 | 的博客
sitemap icon by Icons8
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式