创新实训2024.05.26日志:落地基于硬盘的数据库服务

1. 需求任务列表

以下描述易学大模型软件的web应用的功能。

  1. 用户注册
    1. 用户邮箱,密码,验证码
    2. 开启官方邮箱,用来发验证码(QQ 网易都支持开启smtp协议,找教程,用邮箱不用手机号是为了省买发短信云服务的钱)
    3. 验证码缓存于redis,5min内有效
    4. 验证密码长度,验证码是否正确
    5. 新用户信息保存于mysql,密码使用md5第三方库进行加密
  2. 用户登录
    1. 用户邮箱与密码登录
    2. 校验身份
    3. 根据用户身份信息(也即邮箱,但不要包含密码)生成token
    4. 将token附带在响应中,发回客户端
  3. 用户完善身份信息
    1. 如姓名、年龄、性别
    2. 对自己的简单介绍等
  4. 用户创建专属知识库
    1. 用户可以创建属于自己的知识库,独立于其他用户的知识库存在
    2. 用户可以批量上传任意格式的文件,并将其加入知识库中
  5. 用户创建新会话
    1. 用户可以舍弃之前的上下文,创建一个全新的会话
    2. 该会话用户自己独立的上下文环境
  6. 用户还原历史会话
    1. 用户可以还原之前的历史会话信息,联系上下文继续与大模型chat
  7. 用户进行文件上传会话
    1. 用户可以上传一个或多个文件,但不添加至向量知识库,仅就这几个文件与大模型对话
  8. 用户创建易学知识博客
    1. 仿照csdn等博客网站,可以创建标题,提供博客标签,选择博客封面等进行博客撰写
    2. 支持markdown富文本编辑器
    3. 以markdown格式在网页端显示
  9. 易学贴吧/论坛
    1. 按时间分页显示近期的博客
    2. 用户可以收藏某一篇易学博客
    3. 用户可以在一篇易学博客下进行评论
  10. 管理员后台
    1. 可以查看任意用户的知识库、博客、与大模型的历史会话
    2. 可以封禁某用户及其邮箱,并以邮件的方式通知该用户
    3. 可以解禁某用户及其邮箱,并以邮件的方式通知该用户

2. 硬盘数据库选型

Sqlite(轻量级嵌入式数据库)+SqlAlchemy(Python的orm框架)

Sqlite官网:SQLite Home Page

SqlAlchemy官网:SQLAlchemy - The Database Toolkit for Python

2.1. Sqlite

作为一个嵌入式数据库,Sqlite不以服务进程的方式存在,而是作为文件嵌入在应用进程中。

这里的db文件就是Sqlite的嵌入式数据库文件,每次需要操作数据库时,打开该文件进行curd,操作数据库的会话提交一个事务时(commit),保存修改到该文件。

2.2. SqlAlchemy

SqlAlchemy是Python的一个orm框架,支持多种数据库驱动程序。官方统一地将连接数据库的函数封装了起来(对于所有驱动,都是同一个函数):

from sqlalchemy import create_engine
engine = create_engine("sqlite://", echo=True)

例如sqlite的连接方式就是”sqlite://{你的sqlite的db文件的路径}”。当然,其他数据库也可以,比如通过mysqlconnector进行Mysql数据库的连接。

值得一提的是,SqlAlchemy支持了两种非常强大的机制:类型映射以及依赖管理。此外,他还提供了相当多的语法糖。

类型映射

ORM Mapped Class Configuration — SQLAlchemy 2.0 Documentation

可以将python中的数据类型映射到对应数据库支持的数据类型,采用一个关键词叫做Mapped:

id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str]
fullname: Mapped[str] = mapped_column(String(30))
nickname: Mapped[Optional[str]]

例如,也许你不知道字符串类型在Sqlite数据库中的具体形式,那么你就可以用Mapped[str]来将python的字符串类型自动映射到Sqlite的字符串类型上,同样的,int,bool,byte等等都可以进行映射。

依赖管理

可以说是关系型数据库外键(Foreign Key)的一个重要机制。

假设现在一个用户(User)有多个与大模型的会话(Conversation),那么显然Conversation表中应该包含对应于用户表主键的外键(例如,User.id)。依赖管理机制可以显式地管理这种一对一、一对多、多对一、多对多的依赖。

在User表的python DDL中:

conversations: Mapped[List["Conversation"]] = relationship(back_populates='owner')  # 拥有的会话集合

List[”Conversation”]意味着一个用户可以有多个会话。

在Conversation的python DDL中:

owner: Mapped["User"] = relationship(back_populates='conversations')

“User”意味着一个会话只能属于一个用户。

CURD语法糖

这种有很多,比如增:

session.add(Conversation(id=conv_id, conv_name=nc.conv_name, create_time=datetime.datetime.utcnow(),
                         user_id=nc.user_id))
session.commit()

这里可以通过对象类型反解析出在哪个表插入数据。

查:

result = session.query(Record).filter(Record.conv_id == conv_id).all()

filter过滤器将充当where的条件,all返回所有满足条件的记录的列表。

其他功能,自查官方文档即可。

3. 数据库表设计

基于目前的需求,设计数据库表共七张:

  1. User:用户信息
  2. Conversation:用户与大模型会话信息
  3. Blog:用户博客信息
  4. KnowledgeBase:用户知识库metaData
  5. Comment:用户博客评论信息
  6. Record:用户与大模型对话记录信息
  7. Administrator:管理员信息

3.1. User表

class User(Base):
    __tablename__ = 'User'

    id: Mapped[str] = mapped_column(primary_key=True)
    email: Mapped[str] = mapped_column()  # 一个邮箱只能注册一个账号
    password: Mapped[str] = mapped_column()
    name: Mapped[str] = mapped_column()
    is_active: Mapped[bool] = mapped_column(default=True)  # 是否被封禁
    age: Mapped[int] = mapped_column()
    sex: Mapped[str] = mapped_column()
    description: Mapped[str] = mapped_column()

    conversations: Mapped[List["Conversation"]] = relationship(back_populates='owner')  # 拥有的会话集合
    blogs: Mapped[List["Blog"]] = relationship(back_populates='owner')  # 拥有的博客集合
    comments: Mapped[List["Comment"]] = relationship(back_populates='owner')  # 拥有的评论集合
    knowledge_bases: Mapped[List["KnowledgeBase"]] = relationship(back_populates='owner')  # 拥有的知识库集合

    def __repr__(self):
        return f'<User(id={self.id}, email={self.email}, name={self.name})>'

3.2. Conversation表

class Conversation(Base):
    __tablename__ = 'Conversation'

    id: Mapped[str] = mapped_column(primary_key=True)
    conv_name: Mapped[str] = mapped_column()
    create_time: Mapped[datetime.datetime] = mapped_column(default=datetime.datetime.utcnow())
    user_id: Mapped[str] = mapped_column(ForeignKey('User.id'))

    owner: Mapped["User"] = relationship(back_populates='conversations')
    records: Mapped[List["Record"]] = relationship(back_populates='conversation')

    def __repr__(self):
        return f'<Conversation(id={self.id}, conv_name={self.conv_name})>'

3.3. Blog表

class Blog(Base):
    __tablename__ = "Blog"

    id: Mapped[str] = mapped_column(primary_key=True)
    title: Mapped[str] = mapped_column()
    content: Mapped[Text] = Column(Text)
    create_time: Mapped[datetime.datetime] = mapped_column(default=datetime.datetime.utcnow())
    user_id: Mapped[str] = mapped_column(ForeignKey('User.id'))

    owner: Mapped["User"] = relationship(back_populates='blogs')
    comments: Mapped[List["Comment"]] = relationship(back_populates='blog')

    def __repr__(self):
        return f'<Blog(id={self.id}, title={self.title})>'

3.4. Comment表

class Comment(Base):
    __tablename__ = "Comment"

    id: Mapped[str] = mapped_column(primary_key=True)
    content: Mapped[str] = mapped_column()
    create_time: Mapped[datetime.datetime] = mapped_column(default=datetime.datetime.utcnow())
    user_id: Mapped[str] = mapped_column(ForeignKey('User.id'))
    blog_id: Mapped[str] = mapped_column(ForeignKey('Blog.id'))

    owner: Mapped["User"] = relationship(back_populates='comments')
    blog: Mapped["Blog"] = relationship(back_populates='comments')

    def __repr__(self):
        return f'<Comment(id={self.id}, content={self.content})>'

3.5. KnowledgeBase表

class KnowledgeBase(Base):
    __tablename__ = "KnowledgeBase"

    id: Mapped[str] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column()
    description: Mapped[str] = mapped_column()
    create_time: Mapped[datetime.datetime] = mapped_column(default=datetime.datetime.utcnow())
    user_id: Mapped[str] = mapped_column(ForeignKey('User.id'))

    owner: Mapped["User"] = relationship(back_populates='knowledge_bases')

    def __repr__(self):
        return f'<KnowledgeBase(id={self.id}, name={self.name})>'

3.6. Administrator表

class Administrator(Base):
    __tablename__ = "Administrator"

    id: Mapped[str] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column()
    password: Mapped[str] = mapped_column()

    def __repr__(self):
        return f'<Administrator(id={self.id}, name={self.name})>'

3.7. Record表

class Record(Base):
    __tablename__ = "Record"

    id: Mapped[str] = mapped_column(primary_key=True)
    content: Mapped[str] = mapped_column()
    is_ai: Mapped[bool] = mapped_column()
    conv_id: Mapped[str] = mapped_column(ForeignKey('Conversation.id'))

    conversation: Mapped["Conversation"] = relationship(back_populates='records')

    def __repr__(self):
        return f'<Record(id={self.id}, content={self.content})>'

4. ER图

5. 依赖倒置原则下的数据库连接

仿照Spring的容器机制,我们希望一个组件的生命周期是伴随web应用的启动而创建,伴随web应用的结束而销毁。其中数据库连接就是这样的一个组件。

所有这样的组件,均在component包下,数据库连接引擎只是其中一个:

# DB_engine.py

db_lock = Lock()
engine = None

def init_db_conn():
    global engine, db_lock
    db_lock.acquire()
    engine = create_engine(f'sqlite:///{SQLITE_CONNECTION["location"]}')

    # 注册连接监听 连接时开启外键约束(默认不开启)
    @event.listens_for(engine, "connect")
    def enable_foreign_keys(dbapi_connection, connection_record):
        cursor = dbapi_connection.cursor()
        cursor.execute("PRAGMA foreign_keys=ON;")
        logging.info("Sqlite数据库已开启外键约束")
        cursor.close()

    db_lock.release()

其中,全局变量engine即为数据库连接引擎。在web应用初始化时,我们调用init_db_conn,随后每次操作数据库,均使用该engine。

这里需要注意的是,Sqlite数据库连接时默认不开启外键约束(例如你可以在User根本不存在的情况下插入一条Conversation),因此我们开启连接时的监听,通过SQL语句打开外键约束选项。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/646163.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【软件设计师】先导

一、考试科目&#xff1a; 上午&#xff1a;计算机与软件工程知识&#xff0c;考试时间150min&#xff0c;75空单选题&#xff08;不一定一题一空&#xff09; 下午&#xff1a;软件设计&#xff0c;考试时间150分钟&#xff0c;问答题&#xff0c;6道只做5大题&#xff08;前四…

Nginx 的原理解析 worker 配置及相关问题 -细节狂魔

文章目录 前言Nginx 的最基本的执行过程&#xff08;master & worker&#xff09;worker 是如何进行工作的 一个 master 和 多个 woker 有哪些好处1、可以使用 nginx 热部署2、节省资源 && worker 进程之间互不影响 && nginx 服务不会中断 woker 设置多少才…

简单美观易上手的 Docker Compose 可视化管理器 Dockge

本文首发于只抄博客&#xff0c;欢迎点击原文链接了解更多内容。 前言 Dockge 是 Uptime Kuma 作者的新作品&#xff0c;因此 UI 风格与 Uptime Kuma 基本一致&#xff0c;如果你正在使用 Uptime Kuma 的话&#xff0c;那么 Dockge 的 UI 设计应该也不会让你失望。Dockge 主打…

java人口老龄化社区服务与管理平台源码(springboot+vue+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的人口老龄化社区服务与管理平台。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 人口老龄化…

MySql基础(一)--最详细基础入门,看完就懂啦(辛苦整理,想要宝宝的赞和关注嘻嘻)

前言 希望你向太阳一样&#xff0c;有起有落&#xff0c;不失光彩~ 一、数据库概述 1. 什么是数据库 数据库就是存储数据的仓库&#xff0c;其本质是一个文件系统&#xff0c;数据按照特定的格式将数据存储起来&#xff0c;用户可以对数据库中的数据进行增加&#xff0c;修改&…

Spring中的Aware接口

Spring中的Aware接口 Aware接口介绍 Aware是Spring中的接口&#xff0c;它的作用是可以让Bean获取到运行环境的相关信息。比如获取到上下文、Bean在容器中的名称等。 Spring中提供了很多Aware接口的子类&#xff0c;具体如下&#xff1a; 常用接口的作用如下&#xff1a; …

制作电子画册速成攻略,快来试试

​当今社会&#xff0c;数字媒体日益普及&#xff0c;电子画册作为一种崭新的展示方式&#xff0c;受到了越来越多人的青睐。它不仅形式新颖&#xff0c;互动性强&#xff0c;而且制作起来也并不复杂。想知道如何快速掌握制作电子画册的技巧吗&#xff1f;我来教你吧。 接下来&…

YOLO 学习和使用 (重拾机器学习)

contents a nenrons 单层神经网络 多层神经网络 CNN (Convolutional Neural Network) YOLO 5.1. YOLO(you only look once) 5.2. predict stage: 置信度 * 类别条件概率 全概率非极大值抑制&#xff0c;通过IOU 指数进行实现每个 grid cell 生成两个预测 bounding box 无…

呆马科技----构建智能可信的踏勘云平台

近年来&#xff0c;随着信息技术的快速发展&#xff0c;各个行业都在积极探索信息化的路径&#xff0c;以提升工作效率和服务质量。智慧踏勘云平台是基于区块链和大数据技术构建的全流程智慧可信踏勘解决平台。平台集远程视频、数据显示、工作调度、过程记录为一体&#xff0c;…

【图解IO与Netty系列】IO多路复用

IO多路复用 为什么要使用IO多路复用Linux的IO多路复用接口selectpollepoll 为什么要使用IO多路复用 我们常用的IO模型是BIO&#xff0c;我们Java里的IO流大多数都是BIO&#xff0c;也就是同步阻塞式IO&#xff0c;这种IO操作的好处是简单方便&#xff0c;但是缺点也很明显——…

【雷丰阳-谷粒商城 】【分布式基础篇-全栈开发篇】【00】补充

持续学习&持续更新中… 守破离 【雷丰阳-谷粒商城 】【分布式基础篇-全栈开发篇】【00】补充 WindowsCMD插件IDEAVsCode MavenvagrantDocker解决MySQL连接慢问题启动&#xff08;自动&#xff09;Docker注意切换到root用户远程访问MySQL MyBatisPlus代码地址参考 WindowsC…

vs2013使用qt Linguist以及tr不生效问题

一、qt Linguist&#xff08;语言家&#xff09;步骤流程 1、创建翻译文件,在qt选项中 2.选择对应所需的语言&#xff0c;得到.ts后缀的翻译文件 3.创建.pro文件&#xff0c;并将.ts配置在.pro文件中 3.使用qt Linguist 打开创建好的以.ts为后缀的翻译文件&#xff0c;按图所示…

YOLOv10 | 手把手教你利用yolov10训练自己数据集(含环境搭建 + 参数解析 + 数据集查找 + 模型训练、推理、导出)

一、前言 本文内含YOLOv10网络结构图 各个创新模块手撕结构图 训练教程 推理教程 参数解析 环境搭建 数据集获取等一些有关YOLOv10的内容&#xff01; 目录 一、 前言 二、整体网络结构图 三、空间-通道分离下采样 3.1 SCDown介绍 3.2 C2fUIB介绍 3.3 PSA介绍 …

Java核心: 脚本引擎和动态编译

静态语言和动态语言的在相互吸收对方的优秀特性&#xff0c;取人之长补己之短。脚本引擎和动态编译就是其中一个关键特性&#xff0c;扩展了Java的能力边界。这一篇我们主要讲两个东西: ScriptEngine&#xff0c;执行脚本语言代码&#xff0c;如JavaScript、Groovy JavaCompile…

P459 包装类Wrapper

包装类的分类 1&#xff09;针对八种基本数据类型相应的引用类型——包装类。 2&#xff09;有了类的特点&#xff0c;就可以调用类中的方法。 Boolean包装类 Character包装类 其余六种Number类型的包装类 包装类和基本数据类型的相互转换 public class Integer01 {publi…

关于数据库和数据表的基础SQL

目录 一. 数据库的基础SQL 1. 创建数据库 2. 查看当前有哪些数据库 3. 选中数据库 4. 删除数据库 5. 小结 二. 数据表的基础SQL 1. 创建数据表 2. 查看当前数据库中有哪些表 3. 查看指定表的详细情况(查看表的结构) 4. 删除表 5. 小结 一. 数据库的基础SQL 1. 创建…

Redis篇 redis基本命令和定时器原理

基本命令和定时器原理 一. exists命令二. del命令三. Expire命令四. ttl命令五. redis的过期策略六. 定时器的两种设计方式七. type命令 一. exists命令 用来判断key的值是否存在 返回值是key的个数 这样写的话&#xff0c;有没有什么区别呢&#xff1f; 效率变低&#xff0c;消…

用例篇

弱网测试 弱网测试的目的是尽可能保证用户体验&#xff0c;关注的关键点包括&#xff1a; 页面响应时间是否可以接受&#xff0c;关注包括哦热启动、冷启动时间、页面切换、前后台切换、首字时间&#xff0c;首屏时间等。页面呈现是否完成一致。超时文案是否符合定义&#xf…

YoloV1模型

You Only Look Once 文章目录 You Only Look Once置信度定义类别条件概率NMSnp.maxmiumnp.argsort() Yolov1直接采用网络特征输出&#xff0c;实现置信度预测、分类、边界框回归&#xff1b; 核心内容总结&#xff1a; 输入图像划分为 S S 网格。如果对象的中心落入网格单元中…

Java——认识Java

一、介绍 1、起源 Java 是由 Sun Microsystems 于 1995 年推出的一种面向对象的编程语言和计算平台。由詹姆斯高斯林&#xff08;James Gosling&#xff0c;后来被称为Java之父&#xff09;和他的同事们共同研发。后来&#xff0c;Sun 公司被 Oracle&#xff08;甲骨文&#…