数据库应用系统——外卖平台搭建
实验目的
1. 熟悉开发实现一个数据库应用系统的基本流程。
2. 熟悉数据库操作以及应用与数据库交互过程的编写。
3. 掌握使用数据库作为工具完成访问控制的方法。
4. 掌握防止 SQL 注入的基本方法。
5. 掌握数据库并发、数据备份与恢复等功能的实现。
实验内容
实验任务一:需求分析
在设计数据库应用系统前,需要对此系统进行需求分析。在需求分析中,主要针对数据库进行需求分析,可以采用数据流图、数据字典等方式。
实验任务二:系统设计与数据库设计
在编写数据库应用系统之前,需要对系统和数据库进行总体设计。
根据需求分析的有关内容,画出系统的 E-R 图,并据此设计对应的数据库表结构,然后对系统进行简单范式分析等。
实验任务三:编写数据库应用系统
设计实现一个数据库应用系统,Web、App 等皆可,数据库、语言、平台、框架等可自选,推荐使用 Linux+Apache+PHP+Mysql。有如下要求:
1)用户登录,至少两种不同类型的用户权限访问控制;
2)用户口令哈希存储;
3)体现数据库完整性检查;
4)体现防 SQL 注入;
5)进行系统并发测试;
6)具有数据备份与恢复功能
实验环境
处理器:Apple M1 pro
操作系统:maOS Ventura 13.0.1
开发平台:MacOS + Pycharm + MySQL
环境依赖:
-
Python 3.10
-
Flask 2.3.2
-
PyMySQL 1.0.3
-
MySQL 8.0.33
需求分析
总体分析
需要建立一个外卖订餐平台,为餐饮店与客户提供服务。平台总共有三种用户类型:管理员、商家用户和买家用户,可以实现用户的注册和登陆。
通过管理员登陆的用户可以查看平台上的商家列表以及用户评价,并且拥有移除商家的权限;
商家用户需要看到自己的菜单、用户的评论、订单信息,同时可以修改个人信息;
买家用户可以看到商家、查看评价、菜单、购物车,自己的订单和评价,发表评价以及修改个人信息。
数据字典
| 数据项名 | 名称 | 数据类型 | 长度 | 取值范围 | 取值含义 | 与其他数据的逻辑关系 |
|---|---|---|---|---|---|---|
| 用户名 | username | CHAR | 15 | 用户昵称 | 主键或外键 | |
| 餐厅 | restaurant | CHAR | 15 | 餐厅名字 | 主键或外键 | |
| 菜名 | dishname | CHAR | 15 | 菜品名字 | 主键或外键 | |
| 价格 | price | DECIMAL | (5,2) | 0-99999 | 菜品价格 | |
| 餐厅图片路径 | img_res | VARCHAR | 50 | 0-50 | 图片路径 | |
| 密码 | password | CHAR | 12 | 密码 | ||
| 地址 | address | VARCHAR | 30 | 地址 | ||
| 电话 | phone | CHAR | 15 | 电话号码 | ||
| 菜品信息 | dishinfo | VARCHAR | 50 | 菜的介绍 | ||
| 营养成分 | mutriention | VARCHAR | 30 | 菜的营养成分 | ||
| 售量 | sales | INT | 2 | 菜的销售量 | ||
| 菜品图片路径 | imgsrc | VARCHAR | 50 | 图片路径 | ||
| 是否是招牌菜 | isSpecialty | TINYINT | 1 | 招牌菜的标签 | ||
| 订单号 | orderID | CHAR | 15 | 订单号 | 主键或外键 | |
| 订单完成情况 | isFinished | TINYINT | 1 | 订单完成标签 | ||
| 开销 | cost | DECIMAL | (5,2) | 订单实际花费 | ||
| 评分 | c_rank | TINYINT | 1 | 客户评分 | ||
| 评论 | text | VARCHAR | 50 | 用户评论 | ||
| 交易时间 | transactiontime | TIMESTAMP | 记录评论的订单时间 |
系统设计与数据库设计
系统设计
设计外卖订餐平台“思琪の外卖屋”,平台系统设计如下图所示。

数据库设计
ER图

关系模式
管理员(管理员昵称,密码)
买家用户(用户名,密码,地址,电话)
菜品(菜名,餐厅,餐品信息,营养成分,价格,销量,图片位置,是否是特色菜)
评价(订单号,用户昵称,餐厅,订单是否完成,花费,评分,评价,交易时间)
商家用户(餐厅名称,密码,地址,电话,图片位置)
购物车(用户,餐厅,菜品名称,价格,图片位置)
表结构
总共需要六张表来存储数据:ADMIN(管理员)、CUSTOMER (买家用户)、DISHES(菜品)、 ORDER_COMMENT(评价)、 RESTAURANT(商家用户)、SHOPPINGCART(购物车)。在MySQL中表示如下:

ADMIN需要两类数据:username(管理员昵称)、password(密码):

CUSTOMER需要四类数据:username(用户名)、password(密码)、address(地址)和phone(电话):

DISHES需要八类数据:dishname(菜名)、restaurant(餐厅)、dishinfo(餐品信息)、nutriention(营养成分)、price(价格)、sales(销量)、imgsrc(图片位置)、isSpecialty(是否是特色菜);

ORDER_COMMENT需要8类数据:orderID(订单号)、username(用户昵称)、restaurant(餐厅)、inFinished(订单是否完成)、cost(花费)、c_rank(评分)、text(评价)、transactiontime(交易时间):

RESTAURANT需要5类数据:username(餐厅名称)、password(密码)、address(地址)、phone(电话)、img_res(图片位置):

SHOPPINGCART需要5类数据:username(用户)、restaurant(餐厅)、dishname(菜品名称)、price(价格)、img_res(图片位置):

范式分析
本数据库的关系均为BC范式,因为非主属性对候选键的函数依赖中不存在部分函数依赖或传递函数依赖,且每个依赖的决定因素均为候选键,因此属于BC范式。
实现数据库应用系统
前端页面
本系统的前端页面通过html+css+javascrpt来完成,相关代码在工程文件中的两个文件夹static和templates中。

如下图所示,templates存储页面设计。

static存储动态效果。

最后渲染页面如下:
首页:

注册界面:

登陆界面:

管理员界面:
登陆成功:

登陆失败:

商家列表:

商家界面:
登陆成功:

登陆失败:

首页:

菜单列表:

新增菜品:

修改菜品信息:

个人中心:

修改个人信息:

修改密码:

买家用户:
登陆成功:

登陆失败:

商家列表:

商家菜单:

购物车:

结账:

个人中心:

修改个人信息:

修改密码:

下拉功能:

此外,因为工作量的问题,订单、评价等页面并未完善,在此不予展示。
后端代码
在app.py中,我通过PyMySQL完成与MySQL Server的连接,并借助flask进行Web的搭建,实现前端界面与数据库的交互。因为代码很多,在此就不一一展示,以注册为例,具体实现过程如下:
1 |
|
数据库实现
实现基本数据库
将ER图导出为init.sql,通过执行SQL脚本, 初始化数据库与数据表项。
Init.sql:

执行mysql -u root -p < init.sql,输入密码后,完成基本的数据库创建。因为表比较多,在此只以顾客评论表为例进行展示。


用户口令哈希存储
哈希保存即用户注册时,把他的密码做一次MD5运算储存起来;用户登录时,把他输入的密码做一次MD5运算,再验证是否和数据库里储存的一致。在MD5被证明不够安全以后,大家又开始选择其他的哈希算法如sha256等,这里我仅做MD5处理。以商家客户为例,下面是未经加密的代码。
1 | sql2 = "insert into RESTAURANT (username, password, address, phone) values ('{}', '{}', '{}', '{}') " .format(username, password, addr, phone) |
可以看到密码以明文的形式存储在数据库中,而这存在着极大的安全隐患,因此,在密码保存前进行MD5加密。
1 | sql2 = "insert into RESTAURANT (username, password, address, phone) values ('{}', '{}', '{}', '{}') " .format(username, md5(password), addr, phone) |
这样密码就以MD5哈希值存储在数据库中了,这里可以通过MySQL查看确认。

对应地,登陆的时候,也要将输入的数据进行MD5哈希之后,再与数据库中的值进行对比。
体现数据库完整性检查
数据库操作的完整性检查在实际应用中有非常重要的作用,稍有欠缺,就可能造成严重的后果,因此我增加了数据库操作的限制,比如实体完整性、参照完整性和用户定义的完整性。在此,以评价表中的约束为例。
1 | CREATE TABLE `ORDER_COMMENT`( |
这里我对订单号进行了实体完整性约束,对用户名、餐厅进行了参照完整行约束,并规定了用户名、餐厅、开销、交易时间不能为空,并且交易时间在1970-01-01 00:00:01-2038-01-19 03:14:07之间。
体现防SQL注入
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
有输入的地方就可能存在SQL注入,SQL注入产生的原理归根结底还是用户输入的代码被数据库执行了,所以解铃还须系铃人,防御SQL注入还是要在代码层面上去解决。防止SQL注入的方法有很多,比如采取预编译技术、严格控制数据类型、对特殊的字符进行转义等等,在flask框架中为了防止SQL注入,在请求之前可以加一层参数过滤,以POST请求为例,可以通过以下函数实现:
1 |
|
在对GET请求增加了过滤之后,当输入含敏感字符的参数时,请求将失败:
进行系统并发测试
Flask框架是通过多线程/多进程+阻塞的socket实现非阻塞并发的,其本质是基于python的源库socketserver实现的,即socketserver通过ThreadingMixIn和TCPServer这两个类的组合实现server的多线程非阻塞。比较常用的方式有使用Gunicorn或uWSGI作为WSGI容器,可以将Flask应用部署到多个工作进程上,实现多进程并发处理请求;使用Flask-SocketIO可以将Flask应用扩展为WebSocket应用,可以通过它实现长连接和消息推送功能,从而实现高并发;使用Flask-SQLAlchemy可以将Flask应用与数据库进行集成,可以使用数据库连接池来实现高效的数据库访问,从而提高Flask应用的并发能力;
使用Flask-Cache可以将Flask应用的某些响应结果进行缓存,从而避免重复计算,提升应用的并发能力。以Werkzeug的TCPServer
为例:
1 | class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): |
具有数据备份与恢复功能
有时用户会希望撤销某次操作,这个时候就需要复原回上一个状态,因此数据需要有备份和恢复的功能,我基于MySQL的mysqldumps指令以及Flask的特点,增加备份与恢复功能,主要代码如下。
1 | def dbDump(): |
如下图所示,删除一件商品。

点击撤销本次删除,进行数据恢复。

补充说明
平台使用时,先以MySQL的root身份执行SQL脚本初始化数据库与数据表项(会提示输入root用户的登录密码):
1 | mysql -u root -p \< init.sql |
再执行Web服务启动程序:
1 | python app.py --mysql_pwd 05082966 --db_name appDB |
此处mysql_pwd是MySQL的root用户登录密码,db_name是用init.sql创建的数据库名称。
实验心得
完成一项完整的数据库系统的实现是一项非常有挑战性的任务,从设计到创建再到不断地丰富,我遇到了很多的知识盲区,因此也花了许多的时间去查阅资料、尝试思考以及和同学交流学习,在这个过程中,我学会了很多关于数据库系统设计和实现的知识,同时也遇到了很多困难和挑战。下面是我在完成过程中的一些感想:
首先,在开始做大作业之前,一定要认真阅读作业要求和说明,确保自己完全理解要求和任务,以免走弯路或者遗漏重要要求;
其次,在开始设计和实现数据库之前,需要认真评估不同的数据库系统,以确定哪种数据库最适合你的项目需求。我选择了MySQL数据库,因为它是一种成熟的关系型数据库系统,功能强大,社区支持广泛;
再次,要设计良好的数据库结构,在设计数据库结构时,我灵活运用了各种数据模型和设计工具,并考虑到了数据的完整性、一致性和可扩展性等因素。另外,我还考虑了索引、分区、备份等方面的设计,以提高性能和可维护性。
然后,在实现数据库系统时,我根据设计好的数据库结构,使用了Python编程语言和MySQL数据库管理系统进行开发。在开发过程中,我遇到了很多问题,如性能问题、数据完整性问题等。但是,通过查找资料、与同学交流,我最终成功地解决了这些问题。
最后,我撰写了详细的文档和报告,介绍了我的数据库设计和实现过程,包括数据库架构、数据模型、数据存储和查询性能等方面的内容。我还提供了必要的代码、测试用例和演示等。
通过完成这个数据库系统大作业,我学会了很多关于数据库系统设计和实现的知识,同时也提高了自己的编程技能和解决问题的能力。我也意识到,在完成一个大作业时,规划和执行项目、注重细节和质量、积极与同学或老师交流和讨论都是非常重要的。




