2021年数据库课设该怎么做?一个超市管理系统,简单的前后端分离项目,带你从概要设计走到项目发布!(Vue.js+SpringBoot+MybatisPlus)
2021最详细数据库课设报告,需求分析、概要设计、概念结构设计、物理结构设计、功能模块设计等一应俱全。前后端分离开发Vue.js+SpringBoot+MybatisPlus
由于本文格式原来是word,所以文中有些格式不太对。如果这篇文章对你有帮助,麻烦点赞评论一下谢谢!源码和word文档可私聊领取~
一、相关技术介绍
对使用的RDBMS、应用程序开发环境、系统运行环境、开发工具及版本作简单的介绍。
1.1 RDBMS:
Mysql 5.7.32,Redis 2.8.9(缓存中间件)
1.2应用程序开发环境:
JDK1.8,J2EE
1.3系统开发环境:win10
B/S结构:
后端:springboot+mysql+springcloud+mybatisplus
前端:nodejs + NUXT + elementUI + vue
运维:docker
版本控制:git
1.4 系统生产环境:
Linux CentOS 7.3
(项目部署在阿里云服务器,域名是http://39.108.136.207/#/login)
安全组规则,防火墙端口开放
1.5 简单介绍
1.5.1 API文档
接口信息在启动前后端服务器后访问http://localhost:9004/swagger-ui.html#/
1.5.2 项目维护记录
项目维护的所以记录都放在了gitee码云的远程仓库
前端项目:https://gitee.com/fang-jiale/vue_management_system
后端项目:https://gitee.com/fang-jiale/supermarket_management_system
前端项目的分支,分别是开发对应模块建立出来的。后端只有master分支。
二、需求分析
2.1需求分析:
本次课设,需要完成一个超市管理系统,能够进行增删改查的基本操作,用户在前端网页发送请求时,前端将用户的数据发送到后端,然后查询数据库后响应回去。该系统让用户清楚直观地看到超市内部列表的数据,有权限的用户可以对其进行增删改查功能。
2.2信息需求:
1、用户信息:
用户编号、用户名、密码、真实姓名、性别、出生年月、手机号、注册日期。
2、商品信息:
商品编号、商品名、促销开始时间、促销结束时间、相册描述、促销价格、库存。
3、购买清单信息:
订单编号、收银台编号、商品编号、购买数量、购买日期、用户编号。
4、供应关系信息:
商品编号、供应商编号、供应数量、供应价格、供应日期
5、角色信息:
角色编号、角色名。
- 职员信息:
职员编号、职员名、职员性别、职员年龄、职员职位、薪水、部门编号、收银台编号
-
数据统计信息:
上半年每月销量、商品名称
8、部门信息、供应商信息、收银台信息、仓库信息
…
…
…
2.3处理需求:
1、公共区域
该系统共有四个角色,普通用户,超市员工,超市管理员,DBA都可以看到商品列表和用户列表。但是普通用户只能看商品列表和用户列表,超市员工和超市管理员都可以看除权限管理外的列表,但是员工只有部分增删改权限,DBA拥有全部权限。
2、登录
用户可以通过登录页面登录。
登录时用户必须要填写用户名、密码,通过验证后方可进入对应的界面。
- 增删改操作
由第一点可知,用户根据自己响应的角色只能对自己能够访问的界面的数据进行增删改的操作。
-
数据统计信息
所有用户都可以访问数据统计信息,看到商品销量。
2.4安全性和完整性需求:
1、所有用户均可搜索、访问用户列表和商品列表,超市员工和超市管理员都可以看除权限管理外的列表,但是员工只有部分增删改权限,DBA拥有全部权限。
2、超市员工只可查看后台管理模块所有列表的信息,但是不能修改,超市管理员和DBA可修改信息。
3、每个用户由用户用户编号唯一标识,用户编号由雪花算法生成;用户名不能与其他用户相同,长度是3到10位;用户的密码不允许为空,长度在3到15位;系统自动记录创建时间;
4、超市职员的出生年月在合理范围,且拥有唯一编号;年龄范围在18-66岁之间;
5、每个商品拥有唯一标识;
6、购买清单中的每条元组都有唯一的oid;系统自动记录创建时间;
2.5功能需求:
该程序主要功能需求如下:
-
用户登录:采用用户名及密码验证模式,进入管理系统前,用户必须在登陆页面输入用户名及密码,只有验证通过的用户方可进入超市管理系统操作主页面。
-
用户管理:包括一个功能模块:对学生信息进行增删改查,对用户可用状态的禁用。
-
商品管理:包括两个功能模块:商品清单、购买清单,实现了简单的增删改查功能。
-
后台管理:包括四个功能模块:员工列表、部门列表、供应商列表、供应关系列表,实现了简单的增删改查功能。
-
数据统计:通过查询数据库封装好的表和视图,直观的显示各个商品上半年来的销量趋势变化。
-
权限管理:通过前后端控制,对拥有权限的用户才展示数据。
2.6数据字典:
- 商品清单:commodity
字段名 | 中文字段名 | 字段类型 | 长度 | 主键/外键 | 字段值约束 |
---|---|---|---|---|---|
cid | 商品编号 | char | 13 | P | NOT NULL |
cname | 名称 | char | 10 | NOT NULL | |
price | 基础售价 | int | 10 | NOT NULL | |
promotion_start | 促销开始日期 | date | |||
promotion_end | 促销结束日期 | date | |||
discount_price | 折扣价 | int | 10 | ||
stock | 库存 | int | 10 | NOT NULL | |
on_sale | 是否可售 | smallint | 2 | NOT NULL | |
wid | 仓库号 | char | 10 | F | NOT NULL |
- 部门清单department
字段名 | 中文字段名 | 字段类型 | 长度 | 主键/外键 | 字段值约束 |
---|---|---|---|---|---|
did | 部门编号 | char | 13 | P | NOT NULL |
dname | 部门名称 | char | 10 | NOT NULL | |
population | 人数 | char | 10 | NOT NULL | |
director | 主管 | char | 13 | F | NOT NULL |
主管也是员工,是员工编号的外键
- 员工清单staff
字段名 | 中文字段名 | 字段类型 | 长度 | 主键/外键 | 字段值约束 |
---|---|---|---|---|---|
sid | 员工号 | char | 13 | P | NOT NULL |
sname | 姓名 | char | 10 | NOT NULL | |
ssex | 性别 | char | 2 | NOT NULL | |
sage | 年龄 | smallint | 2 | NOT NULL | |
position | 职称 | char | 10 | NOT NULL | |
salary | 工资 | char | 10 | NOT NULL | |
did | 部门号 | char | 13 | F | NOT NULL |
cashier_id | 收银台号 | char | 13 | F |
- 用户清单user
字段名 | 中文字段名 | 字段类型 | 长度 | 主键/外键 | 字段值约束 |
---|---|---|---|---|---|
id | 用户编号 | char | 30 | P | NOT NULL |
username | 用户名 | char | 20 | NOT NULL | |
password | 密码 | varchar | 200 | NOT NULL | |
mobile | 手机号 | char | 11 | NOT NULL | |
realname | 真实姓名 | char | 10 | NOT NULL | |
accumulated_co nsumption_amount | 累计消费金额 | int | 10 | NOT NULL | |
create_time | 创建时间 | timestamp | NOT NULL | ||
data_status | 禁用状态 | smallint | 2 | NOT NULL | |
last-ip | 上次登录ip | varchar | 16 | ||
Version | 乐观锁 | int | 10 | ||
rid | 角色编号 | Smallint | 6 | F | NOT NULL |
- 供应清单supply
字段名 | 中文字段名 | 字段类型 | 长度 | 主键/外键 | 字段值约束 |
---|---|---|---|---|---|
cid | 商品编号 | char | 13 | P/F | NOT NULL |
supplier_id | 供应商编号 | char | 10 | P/F | NOT NULL |
number | 数量 | int | 10 | NOT NULL | |
supply_price | 供应单价 | char | 10 | NOT NULL | |
date | 供应日期 | datetime | NOT NULL |
- 购买清单sales
字段名 | 中文字段名 | 字段类型 | 长度 | 主键/外键 | 字段值约束 |
---|---|---|---|---|---|
oid | 订单编号 | char | 20 | P | NOT NULL |
cashier_id | 收银台编号 | char | 10 | F | NOT NULL |
cid | 商品编号 | char | 13 | F | NOT NULL |
number | 数量 | int | 10 | NOT NULL | |
date | 购买日期 | datetime | NOT NULL | ||
uid | 用户编号 | int | 13 | F | NOT NULL |
用户会重复买某一商品,在不同日期,所以不是主键
- 供应商清单supplier
字段名 | 中文字段名 | 字段类型 | 长度 | 主键/外键 | 字段值约束 |
---|---|---|---|---|---|
supplier_id | 供应商编号 | char | 10 | P | NOT NULL |
supplier | 供应商名称 | char | 20 | NOT NULL | |
mobile | 电话 | char | 11 | NOT NULL | |
sid | 员工编号 | char | 13 | F | NOT NULL |
一个员工对应多个供应商,一个供应商只对应一个员工。
- 收银台清单cashier
字段名 | 中文字段名 | 字段类型 | 长度 | 主键/外键 | 字段值约束 |
---|---|---|---|---|---|
cashier_id | 收银台号 | char | 10 | P | NOT NULL |
start_time | 每天开始工作时间 | datetime | NOT NULL | ||
end_time | 每天结束工作时间 | datetime | NOT NULL | ||
cashier_ ammount | 台内账户金额 | int | 10 | NOT NULL |
- 仓库清单warehouse
字段名 | 中文字段名 | 字段类型 | 长度 | 主键/外键 | 字段值约束 |
---|---|---|---|---|---|
wid | 仓库编号 | char | 10 | P | NOT NULL |
address | 地址 | char | 20 | NOT NULL | |
sid | 员工编号 | char | 13 | F | NOT NULL |
- 角色列表role
字段名 | 中文字段名 | 字段类型 | 长度 | 主键/外键 | 字段值约束 |
---|---|---|---|---|---|
rid | 角色编号 | smallint | 6 | P | NOT NULL |
role_name | 角色名 | char | 10 | NOT NULL |
2.7数据结构:
名称 | 别名 | 含义 | 组成 |
---|---|---|---|
用户信息 | User表 | 用户的基本信息 | 用户编号+用户名+真实姓名+密码+ 累计消费金额+手机号+禁用状态+注册日期+乐观锁版本号+角色号 |
商品信息 | Commodity表 | 商品的基本信息 | 商品编号+商品名+价格+促销开始时间+促销结束时间+促销价+库存+可售状态+仓库号 |
购买清单信息 | Sales表 | 购买清单的基本信息 | 订单编号+收银台编号+商品编号+ 购买数量+订单创建时间+购买用户的编号 |
仓库信息 | Warehouse表 | 仓库基本信息 | 仓库号+仓库地址+管理仓库的员工编号 |
供应关系信息 | Supply表 | 供应关系信息 | 商品编号+供应商编号+供应商品数量+供应价格+供应日期 |
供应商信息 | Supplier表 | 供应商信息 | 供应商编号+供应商名称+手机号+管理供应商的员工 |
… | … | … | … |
三、概念结构设计
3.1概念模型:E-R图;
3.2应用程序的模块层次结构图:
H图:
3.3关系模型图:
四、逻辑结构设计
4.1 E-R图向关系模型的转换;
4.1.1 数据库模式
商品清单(商品编号、名称、基础售价、促销开始日期、促销结束日期、折扣价、库存、是否可售、仓库号)满足第三范式
部门清单(部门编号、部门名称、人数、主管)满足第三范式
员工清单(员工号、姓名、性别、年龄、职称、工资、部门号、收银台号),满足第三范式
用户清单(用户编号,用户名,密码,手机号,真实姓名,累计消费金额,创建时间,禁用状态,上次登录ip,乐观锁版本,角色编号)满足第三范式
供应清单(商品编号,供应商编号,数量,供应单价)满足第三范式
购买清单(订单编号,收银台编号,商品编号,数量,购买日期,用户编号)满足第三范式
供应商清单(供应商编号,供应商名称,电话,员工编号)满足第三范式
收银台清单(收银台号,每天开始工作时间,每天结束时间,台内账户金额)满足第三范式
仓库清单(仓库编号,地址,员工编号)满足第三范式
角色清单(角色编号,角色名)满足第三范式
4.1.2 数据库外模式
1、商品上半年每月销量视图(商品编号, 商品名称, 月销量)
2、商品上半年销量视图(商品编号, 商品名称, 月销量)
4.2用户子模式(所有视图的定义);
(一月销量)
(半年销量)
定义了各类商品上半年每月销量的视图,还有上半年商品1-6月销量的视图
一月销量视图的查询(其余五月类似):
CREATE VIEW january AS
SELECT DISTINCT s.cid ,c.cname, IFNULL(january,0) january
FROM commodity c,sales s LEFT OUTER JOIN
(SELECT s.cid, c.cname, SUM(number) january
FROM sales s, commodity c
where date BETWEEN ‘2021-01-01’ AND ‘2021-01-31’ AND s.cid = c.cid
GROUP BY s.cid) j ON (s.cid=j.cid)
where c.cid=s.cid
采用左外连接查询和嵌套查询,因为要考虑到即使商品在当月没有销量,也要展示出其的商品名。视图都是建立在基本表上的,所以当购买清单产生或是更改时,视图也会随之更改,可以减少维护。
4.3安全性(用户类别和权限)设计和实现说明;
总共分为四种用户
第一种:root用户,拥有对数据库操作的所有权限,给予DBA使用。
第二种:supermarket-staff,给超市普通职工人员使用,拥有对数据库所有表的查询权限,只对商品和用户的购买清单内的元组有增删改查的功能,对表没有操作权限。
第三种:supermarket-admin,给超市的管理人员使用,拥有对数据库所有表内元组的增删改查权限,**对表没有操作权限(无法增删表等)。**此外,拥有创建视图展示视图的权限。
第四种:user,只有对商品的查询功能和用户列表以及查询数据报表视图的查询功能。
分布对应着角色表的这四个角色。
4.4完整性(主、外码和用户自定义的完整性约束)设计和实现说明
4.4.1 主外码结构
主外码结构如图
主外键建立规则:由ER图易知,实体关系1对多的,外键建立在多的一方,实体关系多对多的,外键建立在中间表里。而主键和外键在上文的数据字典中清楚标识,在此简略书写。外键在更新和删除时的操作都是默认RESTRICT
表名 | 主键 | 外键 |
---|---|---|
cashier | cashier_id | |
commodity | cid | wid:参照warehouse的wid,仓库号 |
department | did | director:参照staff的sid,主管也是员工 |
sales | oid | cid:参照commodity的cid 商品编号 2. id: 参考user的id 用户编号 |
staff | sid | cashier_id 参照cashier的cashier_id 收银台号 did 参照department的did 部门号 |
supplier | supplier_id | sid: 参照staff的sid, 员工号,代表一个供应商由一个员工负责 |
supply | cid, supplier_id | cid:参照commodity的cid 商品编号 supplier_id:参照supplier的supplier_id |
user | id | Rid: 参照role的角色号 |
warehouse | wid | sid: 参照staff的sid 员工号,员工管理仓库 |
role | rid |
4.4.2 用户自定义的完整性
1.定义了非空约束:在上文数据字典中可清楚看到
2.默认值约束:user的version(乐观锁版本号)默认为1,user的data_status(禁用状态),其余所有int类型的数据都默认为0。
3.唯一约束:department的部门名dname,user的用户名username,user的手机号mobile
4.5存储过程
测试并尝试使用了存储过程进行对数据库的操作(插入操作)
4.6触发器
4.6.1 定义的第一种触发器
在用户购买商品后,购买清单sales会增加一条元组,元组包含顾客购买商品的数量,由于有更新操作,所以此时相应的从用户表user中增加用户的累计消费金额,并从commodity商品表中减少对应商品的库存,再然后增加收银台的累计消费金额。考虑到涉及多表更新操作,可以使用触发器。
BEGIN
UPDATE commodity as c SET c.stock =c.stock-(new.number) WHERE c.cid= new.cid
;
UPDATE commodity c,user as u SET u.accumulated_consumption_ammount
=u.accumulated_consumption_ammount + (new.number) * c.price WHERE c.cid=
new.cid AND u.id=new.id;
UPDATE commodity c, cashier as cs SET cs.cashier_ammount =cs.cashier_ammount
+(new.number)* c.price WHERE c.cid= new.cid AND cs.cashier_id=new.cashier_id
;
END
删除时同理。
4.6.2 定义的第二种触发器
在插入职员staff数据时,由于数据中包含职员年龄,应该给其限制范围在18至66岁之间,然而低版本的mysql不支持check约束,所以由触发器实现。
CREATE TRIGGER `insert_sage` BEFORE INSERT ON `staff` FOR EACH ROW begin
if(NEW.Sage<66 AND NEW.Sage >= 18) THEN
SET@choice=1;
ELSE
SET@choice=0;
INSERT INTO staff VALUES(1);
end if;
end;
CREATE TRIGGER `update_sage` BEFORE UPDATE ON `staff` FOR EACH ROW begin
if(NEW.Sage<66 AND NEW.Sage >= 18) THEN
SET@choice=1;
ELSE
SET@choice=0;
INSERT INTO `staff` VALUES(1);
end if;
end;
五、数据库物理设计
5.1所有索引的定义;
使用的存取方法时索引方法中的B+树索引存取方法,按照顺序存储数据,B-TREE会将相关的列值都存储在一起,所以有些查询只使用索引就能完成全部查询,共有三个优点:
-
索引大大减少了服务器需要扫描的数据量。
-
索引可帮助服务器避免排序和临时表。
-
索引可以将随机I/O变为顺序I/O。
所有外键都建立了normal索引,还有部分属性建立了唯一索引
5.2数据的存放位置说明;
由于不同PC机所安装的数据库软件不一定相同,所以数据文件与日志文件的存放位置也不一定相同。
本机存储位置如上
MySQL的每个数据库都对应存放在一个与数据库同名的文件夹中,MySQL数据库文件包括MySQL所建数据库文件和MySQL所用存储引擎创建的数据库文件。
MySQL创建并管理的数据库文件:
.frm文件:存储数据表的框架结构,MySQL数据库文件名与表名相同,每个表对应一个同名frm文件,与操作系统和存储引擎无关,即不管MySQL运行在何种操作系统上,使用何种存储引擎,都有这个文件。
除了必有的.frm文件,根据MySQL所使用的存储引擎的不同(MySQL常用的两个存储引擎是MyISAM和InnoDB),存储引擎会创建各自不同的数据库文件。
MyISAM数据库表文件:
.MYD文件:即MY Data,表数据文件
.MYI文件:即MY Index,索引文件
.log文件:日志文件
5.3系统配置说明;
数据库基于操作系统的,目前大多数MySQL都是安装在Linux系统之上,所以对于操作系统的一些参数配置也会影响到MySQL的性能,下面就列出一些常见的系统配置。由于该项目也部署在了阿里云服务器上(Linux系统
CentOS 7.3),所以配置了以下规则。
5.3.1网络方面的配置,修改/et/sysctl.conf文件
#增加tcp支持的队列数 net.ipv4.tcp_max_syn_backlog = 65535
#减少断开连接时,资源回收 net.ipv4.tcp_max_tw_buckets = 8000
net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_fin_timeout =
10
5.3.2.打开文件数的限制
可以使用ulimit
-a查看目录的限制,可以修改/etc/security/limits.conf文件,增加以下内容以修改打开文件数量的限制
* soft nofile 65535* hard nofile 65535
5.4模块设计(模块IPO图)
5.5 表结构;(钥匙代表主键)
commodity商品表
收银台cashier表
Warehouse 仓库表
User 用户列表
Supply供应关系列表
Supplier 供应商列表
Staff 职员列表
Sales购买清单
Department部门表
Role角色列表
六、功能模块设计
基本的增删改查功能已经完成,在此展示较为复杂的查询功能。
6.1 左外连接、嵌套组合、分组查询
CREATE VIEW january AS
SELECT DISTINCT s.cid ,c.cname, IFNULL(january,0) january
FROM commodity c,sales s LEFT OUTER JOIN
(SELECT s.cid, c.cname, SUM(number) january
FROM sales s, commodity c
where date BETWEEN ‘2021-01-01’ AND ‘2021-01-31’ AND s.cid = c.cid
GROUP BY s.cid) j ON (s.cid=j.cid)
where c.cid=s.cid where c.cid=s.cid
建立所有一月份销量视图的时候,要考虑商品在一月份没有销量也要列出的情况,所以要用左外连接查询把销量为空的商品名也列出来,此外要先查出处于一月时间段的订单,再做连接,所以要使用嵌套查询。
6.2 字符串匹配、模糊、多表连接查询
SELECT c.cname, sl.supplier, s.number, s.date, s.supply_price
FROM commodity c, supplier sl, supply s +
WHERE c.cid = s.cid AND s.supplier_id = sl.supplier_id AND sl.supplier LIKE
'%${queryString}%'
当用户在前端发起请求时(按照供应商名称查询供应列表,queryString封装着供应商名称),因为是模糊查询,所以要用%连接字符串,表示任意长度。
6.3 多表连接、字符串匹配、模糊、聚集函数查询
SELECT count(*)
FROM commodity c, supplier sl, supply s
WHERE c.cid = s.cid AND s.supplier_id = sl.supplier_id AND sl.supplier LIKE
'%${queryString}%'
6.4 limit实现的分页查询
SELECT
id,username,password,mobile,real_name,create_time,accumulated_consumption_ammount,data_status,last_ip,version
FROM user LIMIT (0,9)
前端在刷新页面时把当前页码和页面展示条数发到后端,后端计算数据条数封装到limit进行查询
6.5 事务查询的实现
当用户增加员工时,员工表staff多了一条元组,所以此时也要讲部门表department的部门人数加1,这就是一个事务,要保证其原子性,要么都成功要么都失败,springboot对事务的处理是在方法名上添加@Transactional注解即可。
6.6 项目结构
前端项目的五个功能模块放在五个文件夹中。后端项目共有五个模块,大致分为网关加密、服务注册、后台服务三个功能微服务。
七、数据库实施
7.1数据库及数据库对象的SQL脚本文件:建表语句在附录(报告最后面)
7.2数据库备份和恢复方案
7.21备份策略的设计思路
备份是否完整,能否满足要求,关键还是要看所设计的备份策略是否合理,以及备份操作是否确实按照所设计的备份策略进行了。
针对不同的用途,所需要的备份类型是不一样的,备份策略也各有不同。如为了应对在线应用的数据丢失问题,备份就须要快速恢复,而且最好是只需要增量恢复就能找回所需数据。对于这类需求,最好是有在线的,且部分延迟恢复的备用数据库。因为这样可以在最短时间内找回所需要的数据。甚至在某些硬件设备出现故障的时候,将备用库直接开放,对外提供服务都可以。当然,在资源缺乏的情况下,可能难以找到足够的备用硬件设备来承担这个备份责任,也可以通过物理备份来解决,毕竟物理备份的恢复速度要比逻辑备份快很多。
而对于那些非数据丢失的应用场景,大多数时候恢复时间的要求并不是太高,只要能恢复出一个完整可用的数据库就可以了。所以不论是物理备份还是逻辑备份,影响都不是特别大。
可以根据不同的需求、不同的级别通过如下的几个思路来设计合理的备份策略:
1.对于较为核心的在线应用系统,必须有在线备用主机通过MySQL的复制进行相应的备份,复制线程可以一直开启,恢复线程可以每天恢复一次,尽量让备机的数据延后主机的时间在一定时间段之内。这个延后时间多长合适主要根据实际需求决定,一般来说延后一天是一个比较常规的做法。
2.对于重要级别稍微低一些的应用,恢复时间要求不是太高的话,为了节约硬件成本,不必使用在线的备份主机来单独运行备用MySQL,可以通过一定的时间周期进行一次物理全备份,同时每小时(或者其他合适的时间段)都将产生的二进制日志进行备份。这样虽然没有第一种备份方法恢复快,但是数据的丢失会比较少。恢复所需要的时间由全备周期长短决定。
3.对于恢复基本没有太多时间要求,但是不希望太多数据丢失的应用场景,则可以在一定时间周期内进行一次逻辑全备份,同时也备份相应的二进制日志。使用逻辑备份而不使用物理备份的原因是因为逻辑备份实现简单,可以完全在线联机完成,备份过程不会影响应用提供服务。
4.对于一些搭建临时数据库的备份应用场景,仅仅须要通过一个逻辑全备份即可满足需求,都不须要用二进制日志来进行恢复,因为这样的需求对数据并没有太苛刻的要求。
- (上文部分参考于《MySQL性能调优与架构设计
第五章 MySQL备份与恢复
》)
7.3数据库备份和恢复实践
备份test数据库到G盘的/bak.sql文件下
此时删除test数据库,然后再恢复test数据库。
成功。
根据经验与上课老师所言,应定期进行数据转储,制作后备副本。但转储又是十分耗费时间和资源的,不能频繁进行。DBA应该根据数据库使用情况确定适当的转储周期和转储方法。
数据库转储方案举例: 1.每天晚上进行动态增量转储 2.每周进行一次动态海量转储
3.每月进行一次静态海量转储
7.4日志基本操作
查看错误日志
慢查询日志
八、系统运行与维护
系统使用说明:2-3个程序运行的主要窗口的截图;具有代表性的功能的实现方法
的说明和主要代码(要有较完整的注释),要求能够体现程序设计中的重点和难点及设计者的能力及主要工作。
8.1 界面展示
8.1.1登录界面
8.1.2首页
8.1.3页面展示界面
有多分页展示的界面,结构类似,在此只展示两个主要页面,还有添加用户,编辑用户,删除用户的页面。
8.1.4数据统计界面
展示半年来各个商品的销量趋势
8.2系统代表性的功能
8.2.1雪花算法用户主键
不能使用数据库本身的自增功能来产生主键值,原因是生产环境为分片部署的,可能会分库。使用snowflake
(雪花)算法(twitter出品)生成唯一的主键值
1.41bit的时间戳可以支持该算法使用到2082年
2.10bit的工作机器id可以支持1024台机器
3.序列号支持1毫秒产生4096个自增序列id
4.整体上按照时间自增排序
5.整个分布式系统内不会产生ID碰撞
6.每秒能够产生26万ID左右
8.2.2 数据加密存储
采用BCrypt对数据进行加密后存放到数据库保证私密数据的安全性。BCrypt是使用哈希算法+随机盐来对字符串加密,用户登录验证密码时先将数据库中已经加密的密码取出,在后端服务器解密后再与前端发送的密码进行匹配。
数据库中被加密后的密码如上
8.2.3 接口加密传输
使用RSA对称秘钥算法加密,在前端服务器当用户请求时,用生成的公钥对接口请求的参数进行加密,网关在接收到加密后的数据时,用私钥解密后发到后端服务器进行处理,保证数据传输过程中的安全
前端配置加密的公钥,下面标注行则是调用加密函数把转换成json的数据进行加密
加密前后数据对比如上
8.2.3 并发问题的初步解决
使用乐观锁,对并发的线程冲突进行处理。考虑到可能多用户在同一时间对某用户进行更新操作,引发并发问题,采用乐观锁机制对用户表进行处理。在用户表中添加version字段,每次更新操作成功后,就将version+1。
例如:某元组user原先version值为1,当A线程对user执行了更新操作尚未提交时,B线程对同一user执行更新操作。此时B先完成了更新,version值变为2,此后当A线程commit时,发现version值改变,即撤销当前更新操作,只有B更新成功。
UPDATE user SET username=?, password=?, mobile=?, real_name=?, create_time=?,
accumulated_consumption_ammount=?, data_status=?, last_ip=?, version=? WHERE
id=? AND version=?
Parameters:jisooa(String),
$2a$10$HDuUxCXKrSGPLWePCA7SNeuYYMCptx7PEQxq.OHTaJgncF3qUDC1S(String),
1355987668(String), 金智秀(String), 2020-11-25T13:21(LocalDateTime),
500(Integer), false(Boolean), (String), 3(Integer), 123458434453832323(String),
2(Integer)
(media/3c4a83df6a9956a528abf170ed4e5859.png)]
8.2.4 非关系型数据库Redis的使用
在开发中使用Redis缓存加快数据搜寻,当用户登录浏览器,访问数据时,后端服务器会先查询缓存Redis是否存有数据,若Redis中没有数据,则从数据库中查出数据后,放到缓存中,下次查询时就不用查询数据库,且缓存查询速度很快,减少时间。
需要注意的是,把用户经常查询的数据放到缓存Redis中最好,可以大大提高查询速度,提高用户体验。
例如分页的数据要经常使用,放在Key为UserMap的缓存中,其余几个分页操作类似。
8.2.5 Java Web Token的使用
虽然有了接口加密,但是非法调用者如果知道服务器地址和端口地址的话,直接调用后端服务器接口可能会产生安全问题。
这时候就要在用户登录成功时,后台生成加密的token放到前端服务器中,token的payload中存放用户的id,以后前端每次发起请求时就把token加到请求头,这样只有通过服务器认证的用户才能正常访问后端服务器接口。
token如上
8.2.6 生产环境部署上线
本地开发环境是在Windows10系统下进行的,开发完后将前端服务器的项目build生成dist目录,在阿里云linux服务器上用nginx反向代理vue实现负载均衡部署;
后端服务器打成jar包后,也部署到阿里云服务器上。生产环境是Linux系统,版本是CentOS:7.3,mysql版本是5.7.32。
域名是http://39.108.136.207/#/login
项目采用了SpringCloud的微服务架构,微服务按需注册到eureka注册中心,一个微服务只关注一个特定的业务功能,本项目使用了网关加密微服务和用户后台服务,后续服务还会接着开发。
九、总结
收获与体会
通过课程设计,让我对数据库有了更进一步的认识,数据库操作也不仅仅是增删改查,数据库还有一系列的操作来保证数据的安全性和最小的冗余度,并在多用户同时使用数据时进行并发控制。在数据库设计的过程中,我认为概念模型、逻辑结构设计是最重要的步骤之一,好的概念模型设计可以真实地反应事物之间的联系,而好的逻辑结构则可以减少数据的冗余程度和操作异常。
当然,不止是DB,对DBMS的学习与使用也是让我受益匪浅。不同版本的mysql也对数据操作的支持不太一致,因此要根据实际DBMS的版本去合理使用。
此外,除了DB之外,本次项目采用了前后端分离的开发,虽然工作量很大但是收获和成就感也很大,在开发过程中,应当把系统数据安全放在首位,这次尝试使用了几种数据加密的方法,如jwt的HMAC256加密算法、接口加密的RSA加密算法、密码存储的ECYRPT哈希+盐的算法等。
一个好的项目应该是兼具功能性和好的用户体验,因此学习之路任重而道远。
系统需要改进的地方
-
前后端代码较多,可能有冗余部分。
-
数据库某些表数据量较少,后期会不断增加数据量。
-
对并发的处理只有对用户添加了乐观锁,保证了用户的并发问题。
-
开发时间有限,大部分时间用在开发,对系统的测试不够完善。
-
系统中对事务的处理较少,可能总共就只有两三个事务,一个是用触发器完成的,其余的都是用框架的事务处理功能去处理需要事务的方法。(涉及多表操作,所以是个事务)
十、附录
10.1建表语句
10.1.1 cashier表
CREATE TABLE `cashier` (
`cashier_id` char(10) NOT NULL COMMENT ‘收银台号’,
`start_time` datetime NOT NULL COMMENT ‘每天开始时间’,
`end_time` datetime NOT NULL COMMENT ‘每天结束时间’,
`cashier_ammount` int(10) NOT NULL COMMENT ‘台内账户金额’,
PRIMARY KEY (`cashier_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
10.1.2 commodidy表
CREATE TABLE `commodity` (
`cid` char(13) NOT NULL COMMENT ‘商品编号’,
`cname` char(10) NOT NULL COMMENT ‘名称’,
`price` int(10) NOT NULL COMMENT ‘基础售价’,
`promotion_start` datetime DEFAULT NULL COMMENT ‘促销开始日期’,
`promotion_end` datetime DEFAULT NULL COMMENT ‘促销结束日期’,
`discount_price` int(10) DEFAULT NULL COMMENT ‘折扣价’,
`stock` int(10) NOT NULL COMMENT ‘库存’,
`on_sale` smallint(2) NOT NULL COMMENT ‘是否可售’,
`wid` char(10) NOT NULL COMMENT ‘仓库号’,
PRIMARY KEY (`cid`),
KEY `commodity_wid` (`wid`),
CONSTRAINT `commodity_wid` FOREIGN KEY (`wid`) REFERENCES `warehouse`
(`wid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
10.1.3 department表
CREATE TABLE `department` (
`did` char(13) NOT NULL COMMENT ‘部门编号’,
`dname` char(10) NOT NULL COMMENT ‘部门名称’,
`population` int(6) NOT NULL COMMENT ‘人数’,
`director` char(13) NOT NULL COMMENT ‘主管’,
PRIMARY KEY (`did`),
UNIQUE KEY `unique_dname` (`dname`) USING BTREE,
KEY `dept_director` (`director`),
CONSTRAINT `dept_director` FOREIGN KEY (`director`) REFERENCES `staff`
(`sid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
10.1.4role表
CREATE TABLE `role` (
`rid` smallint(6) NOT NULL,
`role_name` char(10) NOT NULL,
PRIMARY KEY (`rid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
10.1.5 sales表
CREATE TABLE `sales` (
`oid` char(20) NOT NULL COMMENT ‘订单编号’,
`cashier_id` char(10) NOT NULL COMMENT ‘收银台编号’,
`cid` char(13) NOT NULL COMMENT ‘商品编号’,
`number` int(10) NOT NULL DEFAULT ‘0’ COMMENT ‘数量’,
`date` datetime NOT NULL COMMENT ‘日期’,
`id` char(20) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT ‘用户编号’,
PRIMARY KEY (`oid`),
KEY `sales_id` (`id`),
KEY `sales_cid` (`cid`),
CONSTRAINT `sales_cid` FOREIGN KEY (`cid`) REFERENCES `commodity`
(`cid`),
CONSTRAINT `sales_id` FOREIGN KEY (`id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TRIGGER `number_insert` AFTER INSERT ON `sales` FOR EACH ROW BEGIN
UPDATE commodity as c SET c.stock =c.stock-(new.number) WHERE c.cid= new.cid ;
UPDATE commodity c, user as u SET u.accumulated_consumption_ammount
=u.accumulated_consumption_ammount + (new.number) * c.price WHERE c.cid=
new.cid AND u.id=new.id;
UPDATE commodity c, cashier as cs SET cs.cashier_ammount =cs.cashier_ammount
+(new.number)* c.price WHERE c.cid= new.cid AND cs.cashier_id=new.cashier_id ;
END;
CREATE TRIGGER `number_delete` AFTER DELETE ON `sales` FOR EACH ROW BEGIN
UPDATE commodity as c SET c.stock =c.stock+(old.number) WHERE c.cid= old.cid ;
UPDATE commodity c,user as u SET u.accumulated_consumption_ammount
=u.accumulated_consumption_ammount - (old.number) * c.price WHERE c.cid=
old.cid AND u.id=old.id;
UPDATE commodity c, cashier as cs SET cs.cashier_ammount =cs.cashier_ammount
-(old.number)* c.price WHERE c.cid= old.cid AND cs.cashier_id=old.cashier_id ;
END;
10.1.6 staff表
CREATE TABLE `staff` (
`sid` char(13) NOT NULL COMMENT ‘员工号’,
`sname` char(10) NOT NULL COMMENT ‘姓名’,
`ssex` char(2) NOT NULL COMMENT ‘性别’,
`position` char(10) NOT NULL COMMENT ‘职称’,
`sage` smallint(6) NOT NULL COMMENT ‘年龄’,
`salary` char(10) NOT NULL COMMENT ‘工资’,
`did` char(13) NOT NULL COMMENT ‘部门号’,
`cashier_id` char(13) DEFAULT NULL COMMENT ‘收银台号’,
PRIMARY KEY (`sid`),
KEY `staff_did` (`did`),
KEY `staff_cashierId` (`cashier_id`),
CONSTRAINT `staff_cashierId` FOREIGN KEY (`cashier_id`) REFERENCES
`cashier` (`cashier_id`),
CONSTRAINT `staff_did` FOREIGN KEY (`did`) REFERENCES `department`
(`did`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TRIGGER `insert_sage` BEFORE INSERT ON `staff` FOR EACH ROW begin
if(NEW.Sage<66 AND NEW.Sage >= 18) THEN
SET@choice=1;
ELSE
SET@choice=0;
INSERT INTO staff VALUES(1);
end if;
end;
CREATE TRIGGER `update_sage` BEFORE UPDATE ON `staff` FOR EACH ROW begin
if(NEW.Sage<66 AND NEW.Sage >= 18) THEN
SET@choice=1;
ELSE
SET@choice=0;
INSERT INTO `staff` VALUES(1);
end if;
end;
10.1.7 supplier表
CREATE TABLE `supplier` (
`supplier_id` char(10) NOT NULL COMMENT ‘供应商编号’,
`supplier` char(20) NOT NULL COMMENT ‘供应商名称’,
`mobile` char(11) NOT NULL COMMENT ‘电话’,
`sid` char(13) NOT NULL COMMENT ‘员工编号’,
PRIMARY KEY (`supplier_id`),
KEY `supplier_sid` (`sid`),
CONSTRAINT `supplier_sid` FOREIGN KEY (`sid`) REFERENCES `staff` (`sid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
10.1.8 supply表
CREATE TABLE `supply` (
`cid` char(13) NOT NULL COMMENT ‘商品编号’,
`supplier_id` char(10) NOT NULL COMMENT ‘供应商编号’,
`number` int(10) NOT NULL COMMENT ‘数量’,
`supply_price` char(10) NOT NULL COMMENT ‘供应单价’,
`date` datetime NOT NULL COMMENT ‘供应日期’,
PRIMARY KEY (`cid`,`supplier_id`),
KEY `supply_supplierId` (`supplier_id`),
CONSTRAINT `supply_cid` FOREIGN KEY (`cid`) REFERENCES `commodity`
(`cid`),
CONSTRAINT `supply_supplierId` FOREIGN KEY (`supplier_id`) REFERENCES
`supplier` (`supplier_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
10.1.9 user表
CREATE TABLE `user` (
`id` char(20) NOT NULL COMMENT ‘用户编号’,
`username` char(20) NOT NULL DEFAULT ‘’ COMMENT ‘用户名’,
`password` varchar(200) NOT NULL DEFAULT ‘’ COMMENT ‘密码,用户使用’,
`mobile` char(11) NOT NULL DEFAULT ‘’ COMMENT ‘手机号’,
`real_name` char(10) NOT NULL DEFAULT ‘’ COMMENT ‘真实姓名’,
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT ‘创建时间’,
`accumulated_consumption_ammount` int(10) NOT NULL DEFAULT ‘0’ COMMENT
‘累计消费金额’,
`last_ip` varchar(16) DEFAULT ‘’ COMMENT ‘最后登录ip’,
`data_status` tinyint(1) NOT NULL DEFAULT ‘1’,
`version` int(10) DEFAULT ‘1’ COMMENT ‘乐观锁’,
`rid` smallint(6) NOT NULL DEFAULT ‘4’ COMMENT ‘角色id’,
PRIMARY KEY (`id`),
UNIQUE KEY `mobile` (`mobile`),
UNIQUE KEY `username` (`username`) USING BTREE,
KEY `user_rid` (`rid`),
CONSTRAINT `user_rid` FOREIGN KEY (`rid`) REFERENCES `role` (`rid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT=‘用户表’;
10.1.10 warehouse表
CREATE TABLE `warehouse` (
`wid` char(10) NOT NULL,
`address` char(20) NOT NULL,
`sid` char(13) NOT NULL,
PRIMARY KEY (`wid`),
KEY `warehouse_sid` (`sid`),
CONSTRAINT `warehouse_sid` FOREIGN KEY (`sid`) REFERENCES `staff`
(`sid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
10.2建视图语句
10.2.1 一月商品销量视图
CREATE VIEW january AS
SELECT DISTINCT s.cid ,c.cname, IFNULL(january,0) january
FROM commodity c,sales s LEFT OUTER JOIN
(SELECT s.cid, c.cname, SUM(number) january
FROM sales s, commodity c
where date BETWEEN ‘2021-01-01’ AND ‘2021-01-31’ AND s.cid = c.cid
GROUP BY s.cid) j ON (s.cid=j.cid)
where c.cid=s.cid
其余五个月类似,在此不做展示
10.2.2 上半年商品销量视图
CREATE VIEW report1
AS
SELECT DISTINCT j.cid ,j.cname, january,february,march,april,may,june,
(january+february+march+april+may+june)/6 as 月平均销量
FROM january j,february f,march m,april a,may,june
WHERE j.cid=f.cid AND f.cid=m.cid AND m.cid=a.cid AND a.cid=may.cid AND may.cid
= june.cid
更多推荐
所有评论(0)