外卖系统实现
外卖系统实现报告
目录
- 一、实现环境
- 二、系统功能结构图
- 三、基本表的定义和完整性约束
- 四、系统的安全性设计及相关权限
- 五、存储过程、触发器和函数的代码说明
- 六、实现过程中主要技术和主要模块的论述
- 七、运行实例
- 八、源程序说明
- 九、贡献分配
- 十、收获和体会
一、实现环境
我们小组实现了外卖系统。这个系统是一个前端后端分离的项目,前端用了Vue+Elemntui框架,后端用Springboot框架实现,数据库用SQLServer实现。包括前期的设计文档,以及系统实现,前后端联调总共历时两周。最终实现效果不错。
二、系统功能结构图
此系统提供三种身份注册和登录,为商家、顾客、管理员。用户可以在系统上注册商家或者顾客的账号,管理员会对刚注册的账号进行审核。审核通过后用户就能登录刚注册的账号。
对于商家,能够实现:
- 更改商家信息
- 添加、编辑、删除菜品
- 导出所有菜品为Excel表
- 条件查询,排序菜品
- 接受订单,查看订单详细信息
- 业绩统计分析图
对于顾客,能够实现:
- 查看商家列表
- 进入商家查看菜品
- 选择菜品、数目并加入购物车
- 结账并添加备注
- 查看历史订单
对于管理员,能够实现:
- 审核用户
- 审核菜品
- 审核订单
- 日志记录
三、基本表的定义和完整性约束
ID设置表Id_max
| 字段名 | 类型 | 是否允许为空 | 是否唯一 | 字段最大长度 | 内容限制 |
|---|---|---|---|---|---|
| 索引(temp) | int | 否 | 否 | 无 | 0 |
| 订单最大id(Omax) | int | 否 | 否 | 无 | 无 |
| 用户最大id(Umax) | int | 否 | 否 | 无 | 无 |
| 餐品最大id(Fmax) | int | 否 | 否 | 无 | 无 |
| 管理密码(Pwd) | nvarchar | 否 | 否 | 50 | 无 |
商家账户信息表Buser
| 字段名 | 类型 | 是否允许为空 | 是否唯一 | 字段最大长度 | 内容限制 |
|---|---|---|---|---|---|
| ID(Bid) | int | 否 | 是 | 无 | 无 |
| 电话(Btele) | nvarchar | 否 | 否 | 20 | 无 |
| 名字(Bname) | nvarchar | 否 | 否 | 50 | 无 |
| 性别(Bsex) | int | 是 | 否 | 无 | 0男1女 |
| 密码(Bpwd) | nvarchar | 否 | 否 | 50 | 无 |
| 头像(Bimg) | varbinary | 是 | 否 | 无 | max |
| 地址(Badd) | nvarchar | 是 | 否 | 50 | 无 |
| 通过(Bstatus) | nvarchar | 否 | 否 | 20 | 两审核状态 |
| 已删除(Bdel) | int | 否 | 否 | 无 | 0在1无 |
顾客账户信息表Cuser
| 字段名 | 类型 | 是否允许为空 | 是否唯一 | 字段最大长度 | 内容限制 |
|---|---|---|---|---|---|
| ID(Cid) | int | 否 | 是 | 无 | 无 |
| 电话(Ctele) | nvarchar | 否 | 是 | 20 | 无 |
| 名字(Cname) | nvarchar | 否 | 是 | 50 | 无 |
| 性别(Csex) | int | 是 | 否 | 无 | 0男1女 |
| 密码(Cpwd) | nvarchar | 否 | 否 | 50 | 无 |
| 头像(Cimg) | varbinary | 是 | 否 | 无 | max |
| 地址(Cadd) | nvarchar | 是 | 否 | 50 | 无 |
| 通过(Cstatus) | nvarchar | 否 | 否 | 20 | 两审核状态 |
| 已删除(Cdel) | int | 否 | 否 | 无 | 0在1无 |
菜单管理表Food
| 字段名 | 类型 | 是否允许为空 | 是否唯一 | 字段最大长度 | 内容限制 |
|---|---|---|---|---|---|
| ID(Fid) | int | 否 | 是 | 无 | 无 |
| 店家(Bid) | int | 否 | 否 | 无 | 无 |
| 名称(Fname) | nvarchar | 否 | 否 | 50 | 无 |
| 单价(Fprice) | float | 否 | 否 | 无 | 无 |
| 图像(Fimg) | varbinary | 是 | 否 | 无 | max |
| 描述(Fdesc) | nvarchar | 是 | 否 | 100 | 无 |
| 库存(Fstock) | int | 否 | 否 | 无 | 0-100 |
| 分类(Ftype) | nvarchar | 否 | 否 | 20 | 无 |
| 推荐(Frec) | int | 否 | 否 | 无 | 0-5 |
| 状态(Fstatus) | nvarchar | 否 | 否 | 20 | ‘下架’or‘上架’ |
| 审核(Fpass) | nvarchar | 否 | 否 | 20 | 两审核状态 |
| 已删除(Fdel) | int | 否 | 否 | 无 | 0在1无 |
订单表Order_shop
| 字段名 | 类型 | 是否允许为空 | 是否唯一 | 字段最大长度 | 内容限制 |
|---|---|---|---|---|---|
| 订单编号(Oid) | int | 否 | 是 | 无 | 无 |
| 下单时间(Otime) | datatime | 否 | 否 | 无 | 无 |
| 顾客电话(Ctele) | nvarchar | 否 | 否 | 20 | 无 |
| 店家编号(Bid) | int | 否 | 否 | 无 | 无 |
| 顾客编号(Cid) | int | 否 | 否 | 无 | 无 |
| 订单价格(Oval) | float | 否 | 否 | 无 | 无 |
| 状态(Ostatus) | nvarchar | 否 | 否 | 20 | 四接单状态 |
| 备注(Otip) | nvarchar | 否 | 否 | 100 | 无 |
| 已删除(Odel) | int | 否 | 否 | 无 | 0在1无 |
订单详情表Order_food
| 字段名 | 类型 | 是否允许为空 | 是否唯一 | 字段最大长度 | 内容限制 |
|---|---|---|---|---|---|
| 订单编号(Oid) | int | 否 | 否 | 无 | 无 |
| 食品id(Fid) | int | 否 | 否 | 无 | 无 |
| 食品名(Fname) | nvarchar | 否 | 否 | 50 | 无 |
| 食品图(Fimg) | varbinary | 是 | 否 | 无 | max |
| 食品数量(Fnum) | int | 否 | 否 | 无 | 无 |
| 食品总价(Fvalue) | float | 否 | 否 | 无 | 无 |
购物车表Cart
| 字段名 | 类型 | 是否允许为空 | 是否唯一 | 字段最大长度 | 内容限制 |
|---|---|---|---|---|---|
| 店家(Bid) | int | 否 | 否 | 无 | 无 |
| 顾客(Cid) | int | 否 | 否 | 无 | 无 |
| 食品id(Fid) | int | 否 | 否 | 无 | 无 |
| 食品数量(Fnum) | int | 否 | 否 | 无 | 无 |
操作日志Record
| 字段名 | 类型 | 是否允许为空 | 是否唯一 | 字段最大长度 | 内容限制 |
|---|---|---|---|---|---|
| 操作类型(Rtype) | int | 否 | 否 | 无 | 1-10 |
| 操作时间(Rtime) | datatime | 否 | 否 | 无 | 无 |
| 操作用户(Rid) | int | 否 | 否 | 无 | 无 |
| 用户类型(Ukind) | int | 是 | 否 | 无 | 无 |
| 用户电话(Utele) | nvarchar | 是 | 否 | 20 | 无 |
| 用户名字(Uname) | nvarchar | 是 | 否 | 50 | 无 |
| 用户性别(Usex) | int | 是 | 否 | 无 | 0男1女 |
| 用户密码(Upwd) | nvarchar | 是 | 否 | 50 | 无 |
| 用户头像(Uimg) | varbinary | 是 | 否 | 无 | max |
| 用户地址(Uaddr) | nvarchar | 是 | 否 | 50 | 无 |
| 菜品id(Fid) | int | 是 | 否 | 无 | 无 |
| 菜品名称(Fname) | nvarchar | 是 | 否 | 50 | 无 |
| 菜品单价(Fprice) | float | 是 | 否 | 无 | 无 |
| 菜品图片(Fimg) | varbinary | 是 | 否 | 无 | max |
| 菜品描述(Fdesc) | nvarchar | 是 | 否 | 100 | 无 |
| 菜品库存(Fstock) | int | 是 | 否 | 无 | 0-100 |
| 菜品分类(Ftype) | nvarchar | 是 | 否 | 20 | 无 |
| 菜品推荐(Frec) | int | 是 | 否 | 无 | 0-5 |
| 菜品状态(Fstatus) | nvarchar | 是 | 否 | 20 | 2上架状态 |
| 订单id(Oid) | int | 是 | 否 | 无 | 无 |
| 订单状态(Ostatus) | nvarchar | 是 | 否 | 20 | 4接单状态 |
| 店家id(Bid) | int | 是 | 否 | 无 | 无 |
| 备注(Otip) | nvarchar | 是 | 否 | 100 | 无 |
主外码等完整性约束定义
Id_max
1 | constraint primary_Id_max_temp primary key (temp)) |
Buser
1 | constraint primary_Buser_Bid primary key (Bid) |
Cuser
1 | constraint primary_Cuser_Cid primary key (Cid) |
Food
1 | constraint primary_Food_Fid primary key (Fid) |
Order_shop
1 | constraint primary_Oshop_Oid primary key (Oid) |
Order_food
1 | constraint primary_Ofood_Oid_Fid primary key (Oid, Fid) |
Cart
1 | constraint primary_Cart_Bid_Cid_Fid primary key (Bid, Cid, Fid) |
Record
1 | constraint foreign_Record_Bid foreign key (Bid) references Buser(Bid) |
四、系统的安全性设计及相关权限
4.1安全性说明
本系统为外卖系统,各角色(商家、顾客、管理员)的权限较为固定,主要通过前后端接口作为权限划分的依据。
即后端根据前端调用的登录接口与进入商家接口来识别并记录当前用户id及其角色与所在商店id,同时通过前后端接口地址即可确认当前角色以及所需操作(双重保险),在后续的各个接口将当前用户id作为参数参与sql语句,从而在后端代码层面保证每位用户的权限不会“越权”。
4.2权限/外模式设置
商家
查看
1 | 自己店铺的已审核未删除的菜品信息 |
修改
1 | 为自己店铺增加菜品、修改菜品信息 |
顾客
查看
1 | 已审核未删除的商家部分信息 |
修改
1 | 增减、修改自己的购物车 |
管理员
查看
1 | 未删除的商家信息 |
修改
1 | 审核、删除商家 |
五、存储过程、触发器和函数的代码说明
5.1存储过程
使用经典的insert与update进行数据库更新,所有删除使用del属性进行标记
在java中书写sql代码,使用Connection.prepareStatement.setBlob将二进制图像插入sql语句
1 | prest = con.prepareStatement("insert into Food values (" |
使用Connection.Statement进行简单sql语句运行
1 | sql = "update Food set Fname = '" + food + "', Fprice = " + price + |
5.2 Insert触发器
防止插入Food表时Fstock超出约束性定义[0,100]的限制造成约束性错误
1 | create trigger insertFood on Food instead of insert |
5.3Update触发器
防止修改Food表时Fstock超出约束性定义[0,100]的限制造成约束性错误
1 | create trigger updateFood on Food instead of update |
5.4函数
实现直接对float型数据进行string与float之间的转换,与前后端接口处价格等float数据使用string类型相适应
1 | create function FloatToStr(@num float) |
1 | create function StrToFloat(@num nvarchar(20)) |
六、实现过程中主要技术和主要模块的论述
6.1前后端交流的基地址设置
1 |
|
6.2后端springboot与数据库sqlserver连接
使用idea新建springboot项目,用自带的添加数据源进行初步连接
接着使用代码进行数据库连接与sql语句编写运行,需要注意设置trustServerCertificate,否则可能连接失败
1 | private final String dbUrl ="jdbc:sqlserver://localhost:1433;database=homework;trustServerCertificate=true"; |
对于查询得到的ResultSet表格,可以迭代遍历(rs,next)取用(rs.getInt())
1 | if(rs.next()) { |
需要注意的是插入varbinary二进制图片数据时,无法将byte[]数据直接转化为string并插入sql语句,需要作为额外参数插入语句
1 | PreparedStatement prest; |
6.3前后端连接
使用@RestController定义控制类作为总的接受前端接口的类
使用@CrossOrigin避免可能出现的跨域报错
@PostMapping("/initial/register")或者Get/Put/Delete接受相应的接口请求,当接收到前端请求时即会调用想用的函数,如下当接收到/initial/register的Post请求时会调用register函数,@RequestBody接收前端传过来的json参数,为了代码、理解方便而令前端使用字典格式,在register函数内部可以对数据库进行操作获取数据,@ResponseBody将后端返回的数据自适应为前端的数据格式。
1 |
|
6.4模块论述
从逻辑上基本可以分为登录模块、菜单模块、订单模块、管理模块
6.4.1登录模块
注册时,通过前端传到后端的账号类别、手机号、密码等信息与账户信息表中的select数据进行对比,若存在重复名称或重复手机号则注册失败,注册成功后等待管理员审核后即可登录;
登录时若找到匹配的数据,则登录成功,同时更新当前用户信息,记录当前使用者的信息,以确认具体角色权限,同时若使用者为用户,则可以通过进入商店接口确认用户当前所浏览的是那个商店界面,从而进行针对商店的用户操作(例如购物车);
修改密码和登录同理,若匹配则运行update指令更新密码;
用户ID根据Id_max表格中的Umax记录作为当前历史最大的用户数,并将Umax加一,可以理解为ID每次递增加一,顾客与商家共用Umax。
6.4.2菜单模块
商家添加新菜品时,检测数据库中是否存在未删除的同名菜品,若存在,则添加失败,添加成功后等待管理员审核;
修改菜品信息时,同样检测同名菜品判断是否修改失败;
菜品ID与用户ID同理。
6.4.3订单模块
主要通过顾客向购物车添加菜品,若菜品库存不足添加量,则将已有库存先加入购物车后再返回库存不足的提醒,在添加购物车时就同步减少相应库存;
顾客提交购物车后,即将当前购物车清空,并构建菜单,等待管理员审核,管理员审核后商家即可接单;
查询订单可可以通过select where限定ID,时间,商家,顾客等多个属性进行筛选;
订单ID与用户ID同理。
6.4.4管理模块
管理员由于具有唯一性,因不设定用户名,仅考虑登录密码是否正确;
管理员不可注册,密码通过后端直接在数据库中设置;
通过select各个对象的表格的信息传回前端,根据前段审核或删除指令更新对象的审核状态与删除状态这两个参数;
对于已经删除的对象,永远无法被任何人再次读取,通过选择筛选del = 0来保证这一点;
每一次改变数据库的操作都会被记录在日志中,日志中保存了操作时间、操作类别、操作具体数据。
七、运行实例
登陆界面
用户经过注册,等待管理员审核通过后才能登录。登录只需要输入注册时的用户名和密码,然后选择对应的类型即可。
注册界面
注册界面需要输入用户名,联系电话,注册类型,性别,密码以及地址。
其中用户名和联系电话都会进行约束性条件检查,要求手机号第一位为1,以及总位数为11位,密码为6-15个字符之间等等。
同时我们采用了百度地图的获取当前位置的方式能够获取到当前所在的省市以及经纬度。
当前注册一个用户名为ioio,密码为ioioio的商家账号。
现在登录管理账号审核,可以发现当前帐号已经出现在所有用户中,并且是未审核的状态,将其通过审核之后这个账号就能正常登录。
商家页面
商家简介
登录已创建的商家之后,可以看到商家中心的简介:
点击圆形图片可以更换头像,头像下面显示了当前商家的名称和地址,以及右侧有一个当前商家的评分、店铺分类、营业时间等,可以点击编辑按钮对其进行编辑:
同时点击“账户信息”,会显示当前账户的用户名,注册时的手机号以及地址等信息,同样可以进行编辑:
销售业绩
销售业绩部分可以看到当前商家的客户总数、订单总数、销售额以及商品总数,以及对于四者对应过去的一周业绩的曲线图:
商品管理
可以看到所有的商品列表,能够实现图片显示,并且可以选择不同的条数进行翻页。
将左侧的选择改为菜品名,在输入框中填写牛肉面,再点击搜索,可以根据输入框进行条件查询:
注:这是通过后端接口,即数据库的SELECT语句实现的,而非通过前端过滤。
在查询菜品列表的时候有函数作用使得将float类型的价格转换为nvarchar从而与接口类型匹配。
点击“添加菜品”按钮,可以实现菜品的添加,可以设置其价格,库存,推荐指数,图片以及描述等信息:
添加菜品之后经过管理员审核之后菜品就会显示在菜品列表。
点击每一行的“编辑”按钮之后,就能实现对该菜品的编辑,在编辑的过程中,在每次增加或者修改菜品的时候后端有触发器保证stock库存始终处于[0,100]之间。
如果将该菜品更改为下架,那么顾客端口就不会显示该商家的该商品:
点击“删除”按钮,可以实现对菜品的删除。
点击“筛选”按钮,可以实现对菜品的多条件筛选,即输入菜品价格上下限,库存的上下限以及类型:
点击“导出Excel表”还可以将当前商家的所有菜品导出为Excel表:
订单管理
订单管理页面可以实现对订单编号/订单人姓名的条件查询,同样也可以导出为Excel表:
对于每一个订单,都会在左栏显示下单的时间,下单人姓名、下单金额以及备注。
点击任意一行,就会在右边的“订单详情”板块显示当前订单的状态,订单的编号,联系电话,下单时间,顾客昵称,下单金额以及详细的菜品,以及备注:
订单最初始的状态是未通过的状态,此时等待管理员审核通过之后,就会出现在商家的订单列表里。商家点击“未接单”的红色按钮之后,就会显示“已接单”的状态,同时该信息也会同步给顾客,顾客收到订单之后点击“已完成”之后,商家这边的“订单详情”就会显示“已完成”的状态。
还可以看到当前商家的统计详情,用的是echarts组件,会标记出过去12个月的每个价格区间内的销售额以及月平均销售额,方便商家对各个价位的菜品进行评估:
顾客
所有商店
顾客可以查看商家列表,了解商家名称,位置,联系方式等:
点击操作按钮,可以进入到商家的菜品页面:
在菜品页面可以查看菜品信息(名称、价格、图片等),通过右侧数量选择输入或点击上下箭头调整数量,
在最下方点击加入购物车按钮可以将选择的菜品加入购物车:
购物车
点击购物车选项可以查看已经添加在购物车中的菜品:
之后可以选择删除菜品、添加备注、结账:
历史订单
进入历史订单页面可以看到过往的所有订单,并可以根据时间排序:
管理员
管理员的主要职责有对用户的审核,删除,同时可以在输入框中进行全局搜索:
对所有菜品的审核,删除,同时可以在输入框中进行全局搜索:
全局搜索“牛肉面”之后的结果如下图所示:
对订单的审核,删除, 同时也可以进行全局搜索:
并且可以查看每个订单的具体信息:
也可以进行日志查看,日志包括所有的操作、时间以及对应操作的ID(商家和顾客的ID不能重复):
八、源程序说明
8.1后端基本框架
8.1.1构建数据库
通过BuildDatabase检查是否存在总数据库与所需表姑,若不存在则构建总数据库与表格
1 | String sql = "select name from sys.databases where name = 'homework'"; |
8.1.2接受前端端口数据并运行代码
Controller类作为总的接口类,接受各个接口,并将不同接口的代码分别封装在Admin/Business/Customer/Initial/Log的类方法中,在Controller直接调用类方法并返回即可
8.2前端基本框架
采用了Vue前端框架和Element Ui框架。
其中也用了eCharts等组件。teacher中记录的是商家页面。student中记录的是顾客页面。admin中记录的是管理员页面。
九、收获和体会
实现的系统基本实现了外卖系统应该能够实现的任务,例如点餐,下单等基本功能。系统具有一定的规模,且在开发过程中遇到了很多困难,从最初的如何选题,如何设计系统,到之后的前后端语言和架构的学习,到最后的前后端联调部分,组员的每一位同学都积极参与和互相协调,最终解决了这些问题。在这次的系统开发过程中,我们学习到了很多最初没有接触过的知识,通过学习应用到实践过程中去,并将上课时学习到的SQL语句以及系统架构等知识运用到其中。
附件:数据库前后端API.pdf