
Database System Concepts
超市管理数据库设计
Student ID: 21307381 Student Name: LJW
Date: 2023.12.12
Lectured by: 潘嵘
Database System Concepts Course
Sun Yat-sen University
超市管理数据库设计
一、引言
要求
以Postgresql、SQL Server或My SQL等作为后台数据库,以React、Vue、Angular等或其它开发工具作为前台开发工具,完成一个小型数据库应用系统的设计开发。
(1)针对给定的系统进行需求分析,设计系统结构图和系统功能模块图;
(2)针对需求分析,画出E-R图表示的概念模型,并将其转换为至少满足3NF的关系模式,设计较为合理的数据库模式;
(3)系统中应能体现对数据库的保护(安全性、完整性等);
(4)系统应该有较为友好的用户界面;
设计目的
设计一个超市库存管理系统,包括商品信息管理、进货管理、销售管理、库存管理等功能。进货管理负责商品的进货录入、修改和查询;销售管理负责商品的销售录入、修改和查询;库存管理负责商品库存的查询和预警。
设计环境
Pycharm:Python的集成开发环境(IDE);
DataGrip:数据库集成开发环境(IDE);
Mysql:数据库
Node.js:基于 Chrome V8 引擎的 JavaScript 运行环境;
Vue.js:Vue.js是一个流行的JavaScript框架,用于构建交互式的Web应用程序;
Visual Studio Code(VS code):用于编写Vue的前端代码;
ElementUI:基于Vue.js的组件库,提供了一套丰富的UI组件和工具,可以帮助我们快速构建漂亮、响应式的Web界面。
二、概要设计
系统需求分析
随着现代商业的快速发展,超市的库存管理变得越来越复杂,传统的手工管理方式已经无法满足现代化企业的需求。因此,需要一个更加智能化、高效化的库存管理系统来帮助企业进行有效的库存管理。超市库存管理系统可以帮助企业实时监控库存情况,避免因为库存不足而影响销售业绩,同时也可以防止过多的库存积压导致资金占用和商品过期等问题。此外,它还可以提供精准的数据支持,帮助企业更好地掌握市场需求和产品销售情况,优化供应链管理,提高企业的运营效率和盈利能力。
因此,超市库存管理系统的设计与实现具有重要的现实意义和应用价值,需要包括商品信息管理、进货管理、销售管理、库存管理、员工管理的功能。商品信息管理包括商品基本信息和库存的多少及库存预警;进货管理负责商品的进货录入、修改和查询;销售管理负责商品的销售录入、修改和查询;客户管理负责客户的录入、修改和查询,员工管理负责员工的录入、修改和查询。
系统应当在库存低于设定值时触发库存预警,并且可以在销售以及进货后自动更改商品的库存信息。同时,系统应当具备可扩展性,以便添加新的功能。系统的用户界面应当清晰明了,易于操作和导航。用户界面设计应当符合用户习惯,提供直观的操作方式和友好的交互体验。
系统结构设计
- 前端设计: 前端部分采用Vue.js框架进行开发,Vue.js是一款流行的JavaScript框架,能够帮助我们构建交互式的用户界面。通过Vue.js,我们可以实现页面的动态数据绑定、组件化开发等特性,使得用户界面更加友好、响应更加迅速。
- 后端设计: 后端采用Flask框架进行开发,Flask是一个轻量级的Python Web框架,适合快速开发Web应用程序。我们可以利用Flask框架来处理前端请求、实现业务逻辑、访问数据库等功能。通过Flask提供的路由、视图函数等特性,可以构建出高效稳定的后端服务。
- 数据库设计: 数据库部分采用MySQL进行存储和管理,MySQL是一个开源的关系型数据库管理系统,具有稳定性高、性能优异等特点。我们可以在MySQL数据库中创建商品表、进货表、销售表等相关表格,用于存储超市库存管理系统所需的各种数据信息。
- 运行环境: 超市库存管理系统采用B/S结构设计,该系统在浏览器上运行,用户可以通过浏览器访问系统,进行商品信息管理、进货管理、销售管理和库存管理等操作。前端页面通过Vue.js进行渲染和交互,后端通过Flask处理前端请求并与MySQL数据库交互,从而完成整个超市库存管理系统的功能。
- 交互流程: 用户在浏览器上输入系统地址访问系统,在前端页面上进行操作,如添加商品、录入进货信息、录入销售信息等。前端页面将用户的操作请求发送给后端,后端根据请求内容进行相应的处理,并与数据库进行数据交互。最终,前端页面获取后端返回的数据,进行展示和交互,完成用户的操作需求。
通过以上系统结构设计,我们将实现一个基于Vue.js和Flask的前后端分离架构,以及MySQL数据库的超市库存管理系统。这样的架构设计将使系统更具灵活性、可维护性和可扩展性,为用户提供良好的使用体验。
功能模块设计
超市库存管理系统是一个用于实现超市商品库存管理的软件系统。该系统主要包括商品信息管理、进货管理、销售管理和负责的员工管理和可乎管理功能模块。下面对每个功能模块进行详细说明:
- 商品信息管理:
- 添加商品:管理员可以添加新商品到系统中,包括商品名称、单价、库存等信息。
- 修改商品信息:管理员可以针对已存在的商品进行修改,如修改商品名称、价格等。
- 删除商品:管理员可以删除不再销售的商品信息。
- 查询商品:用户可以通过商品ID、名称等关键字进行商品查询,获取商品的详细信息。
- 进货管理:
- 进货录入:管理员可以录入商品的进货信息,包括商品ID、进货数量、进货时间等。
- 修改进货信息:管理员可以对已录入的进货信息进行修改,如修改进货数量、进货时间等。
- 查询进货信息:用户可以根据进货ID、商品ID等关键字进行进货信息的查询。
- 销售管理:
- 销售录入:收银员可以录入商品的销售信息,包括商品ID、销售数量、销售时间等。
- 修改销售信息:管理员可以对已录入的销售信息进行修改,如修改销售数量、销售时间等。
- 查询销售信息:用户可以根据销售ID、商品ID等关键字进行销售信息的查询。
- 库存管理:
- 查询库存:用户可以根据商品ID、名称等关键字进行库存查询,获取商品的实时库存信息。
- 库存预警:系统可以根据设定的库存预警值,在商品库存低于预警值时进行提醒。
- 员工管理:
- 查询员工:用户可以根据员工ID、名称等关键字进行员工查询,获取负责进货员工信息,包括员工电话、员工部门等,以便及时通知进货。
- 添加员工:管理员可以添加新员工到系统中。
- 删除员工:开除员工。
通过该系统,超市管理员能够方便地管理商品信息、进行进货和销售操作,并及时掌握商品的库存情况。同时,系统还提供了库存预警功能,帮助管理员做出及时的补货决策,确保商品供应充足,提升用户体验。
三、详细设计
系统数据库设计
1.客户表client
列名含义 | 列名名称 | 数据类型 | 长度/精度 | 是否为空 | 约束 |
---|---|---|---|---|---|
客户编号 | client_id | char | 10 | no | primary key |
客户姓名 | client_name | varchar | 20 | no | |
联系方式 | phone_number | varchar | 11 | unique | |
地址 | address | varchar | 10 |
2.员工表staff
列名含义 | 列名名称 | 数据类型 | 长度/精度 | 是否为空 | 约束 |
---|---|---|---|---|---|
员工编号 | staff_id | char | 10 | no | primary key |
员工姓名 | staff_name | varchar | 20 | no | |
工作部门 | department | varchar | 10 | no | check:”purchase” or “sale”(触发器) |
薪资 | salary | decimal | (8,2) | no | check≥0 |
联系方式 | phone_number | varchar | 11 | unique |
3.商品信息表goods
列名含义 | 列名名称 | 数据类型 | 长度/精度 | 是否为空 | 约束 |
---|---|---|---|---|---|
商品编号 | goods_id | char | 10 | no | primary key |
商品名称 | goods_name | varchar | 20 | no | |
商品数量(库存数量) | goods_num | int | DEFAULT 0, check(≥0) |
4.进货信息表purchase
列名含义 | 列名名称 | 数据类型 | 长度/精度 | 是否为空 | 约束 |
---|---|---|---|---|---|
进货编号 | purchase_id | char | 10 | no | primary key |
进货员工编号 | staff_id | char | 10 | no | foreign key |
商品编号 | goods_id | char | 10 | no | foreign key |
进货单价 | purchase_price | decimal | (6,2) | no | check≥0 |
进货数量 | purchase_num | int | no | check>0 | |
进货金额 | purchase_amount | decimal | (8,2) | no | 进货单价*进货数量 (触发器) |
进货日期 | purchase_date | date | no |
5.销售信息表sale
列名含义 | 列名名称 | 数据类型 | 长度/精度 | 是否为空 | 约束 |
---|---|---|---|---|---|
销售编号 | sale_id | char | 10 | no | primary key |
客户编号 | client_id | char | 10 | no | foreign key |
商品编号 | goods_id | char | 10 | no | foreign key |
销售单价 | sale_price | decimal | (6,2) | no | check≥0 |
销售数量 | sale_num | int | no | check>0 | |
销售金额 | sale_amount | decimal | (8,2) | no | 销售单价*销售数量 (触发器) |
销售日期 | sale_date | date | no |
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
-- 1. 删除表,保证是我们设计的最新的表
DROP TABLE IF EXISTS purchase;
DROP TABLE IF EXISTS sale;
DROP TABLE IF EXISTS staff;
DROP TABLE IF EXISTS client;
DROP TABLE IF EXISTS goods;
-- 2. 创建客户表
CREATE TABLE client (
client_id CHAR(10) PRIMARY KEY NOT NULL,
client_name VARCHAR(20) NOT NULL,
phone_number VARCHAR(11) UNIQUE,
address VARCHAR(10)
);
-- 3. 创建员工表
CREATE TABLE staff (
staff_id CHAR(10) PRIMARY KEY NOT NULL,
staff_name VARCHAR(20) NOT NULL,
department VARCHAR(10) NOT NULL,
salary DECIMAL(8,2) NOT NULL ,
phone_number VARCHAR(11) UNIQUE,
CHECK (salary>=0)
);
-- 4. 创建商品表
CREATE TABLE goods (
goods_id CHAR(10) PRIMARY KEY NOT NULL,
goods_name VARCHAR(20) NOT NULL,
goods_num INT DEFAULT 0,
CHECK (goods_num >= 0)
);
-- 5. 创建进货表
CREATE TABLE purchase (
purchase_id CHAR(10) PRIMARY KEY NOT NULL,
goods_id CHAR(10) NOT NULL ,
staff_id CHAR(10) NOT NULL ,
purchase_price DECIMAL(6,2) NOT NULL ,
purchase_num INT NOT NULL ,
purchase_amount DECIMAL(8,2) NOT NULL ,
purchase_date DATE NOT NULL ,
CONSTRAINT fk_goods1 FOREIGN KEY (goods_id) REFERENCES goods(goods_id),
CONSTRAINT fk_staff FOREIGN KEY (staff_id) REFERENCES staff(staff_id),
CHECK (purchase_price >= 0),
CHECK (purchase_num >= 0),
CHECK (purchase_amount = purchase_price * purchase_num)
);
-- 6. 创建销售表
CREATE TABLE sale (
sale_id CHAR(10) PRIMARY KEY NOT NULL,
client_id CHAR(10),
goods_id CHAR(10),
sale_price DECIMAL(6,2),
sale_num INT,
sale_amount DECIMAL(8,2),
sale_date DATE,
CONSTRAINT fk_client FOREIGN KEY (client_id) REFERENCES client(client_id),
CONSTRAINT fk_goods2 FOREIGN KEY (goods_id) REFERENCES goods(goods_id),
CHECK (sale_price >= 0),
CHECK (sale_num >= 0),
CHECK (sale_amount = sale_price * sale_num)
);
ER图如下:
根据第三范式(3NF)的定义,一个关系模式 R 满足 3NF,当且仅当 R 中的每一个非主属性都不传递依赖于 R 的任何一个候选码。在我们设计的超市库存管理数据库中:
- 客户表 client:该表中只有客户编号(client_id)是主键,其他字段都依赖于客户编号,因此满足 1NF 和 2NF,同时也满足 3NF。
- 员工表 staff:该表中员工编号(staff_id)是主键,工作部门(department)和薪资(salary)都只依赖于员工编号,因此满足 1NF 和 2NF,同时也满足 3NF。
- 商品信息表 goods:该表中商品编号(goods_id)是主键,商品名称(goods_name)和商品数量(goods_num)都只依赖于商品编号,因此满足 1NF 和 2NF,同时也满足 3NF。
- 进货信息表 purchase:该表中进货编号(purchase_id)是主键,进货员工编号(staff_id)、商品编号(goods_id)、进货单价(purchase_price)、进货数量(purchase_num)和进货日期(purchase_date)都只依赖于进货编号,同时进货金额(purchase_amount)通过触发器计算,因此满足 1NF 和 2NF,同时也满足 3NF。
- 销售信息表 sale:该表中销售流水号(sale_id)是主键,客户编号(client_id)、商品编号(goods_id)、销售单价(sale_price)、销售数量(sale_num)和销售日期(sale_date)都只依赖于销售流水号,同时销售金额(sale_amount)通过触发器计算,因此满足 1NF 和 2NF,同时也满足 3NF。
综上所述,数据库设计满足第三范式。
为了保证数据库的安全性和完整性,还添加了以下几个触发器:
- 插入或更新员工表时,工作部门(department)必须是 sale or purchase;
- 插入或更新进货表时自动计算进货的总金额=进货单价*进货数量,即使输入错误或者不输入也可以自动补全;
- 插入或更新销售表时自动计算进货的总金额=销售单价*销售数量,即使输入错误或者不输入也可以自动补全;
- 插入或更新销售表和进货表时,自动更新商品信息表的商品数量(goods_num)。
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
-- 触发器
-- 1. 工作部门必须是 sale or purchase
-- 限制插入
DROP TRIGGER IF EXISTS enforce_department_insert_constraint;
DELIMITER //
CREATE TRIGGER enforce_department_insert_constraint
BEFORE INSERT ON staff
FOR EACH ROW
BEGIN
IF NEW.department != 'purchase' AND NEW.department != 'sale' THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Insert Department must be "purchase" or "sale"';
END IF;
END //
DELIMITER ;
-- 限制更新
DROP TRIGGER IF EXISTS enforce_department_update_constraint;
DELIMITER //
CREATE TRIGGER enforce_department_update_constraint
BEFORE UPDATE ON staff
FOR EACH ROW
BEGIN
IF NEW.department != 'purchase' AND NEW.department != 'sale' THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Update Department must be "purchase" or "sale"';
END IF;
END //
DELIMITER ;
-- 2. 插入或更新时自动给计算进货的总金额=进货单价*进货数量
DROP TRIGGER IF EXISTS calculate_insert_purchase_amount;
DELIMITER //
CREATE TRIGGER calculate_insert_purchase_amount
BEFORE INSERT ON purchase
FOR EACH ROW
BEGIN
SET NEW.purchase_amount = NEW.purchase_price * NEW.purchase_num;
END //
DROP TRIGGER IF EXISTS calculate_update_purchase_amount;
CREATE TRIGGER calculate_update_purchase_amount
BEFORE UPDATE ON purchase
FOR EACH ROW
BEGIN
SET NEW.purchase_amount = NEW.purchase_price * NEW.purchase_num;
END //
DELIMITER ;
-- 3. 插入或更新时自动给计算销售的总金额=销售单价*销售数量
DROP TRIGGER IF EXISTS calculate_insert_sale_amount;
DELIMITER //
CREATE TRIGGER calculate_insert_sale_amount
BEFORE INSERT ON sale
FOR EACH ROW
BEGIN
SET NEW.sale_amount = NEW.sale_price * NEW.sale_num;
END //
DROP TRIGGER IF EXISTS calculate_update_sale_amount;
CREATE TRIGGER calculate_update_sale_amount
BEFORE UPDATE ON sale
FOR EACH ROW
BEGIN
SET NEW.sale_amount = NEW.sale_price * NEW.sale_num;
END //
DELIMITER ;
-- 4.插入进货表时自动更新商品信息
DELIMITER //
DROP TRIGGER IF EXISTS update_goods_num_purchase_insert;
CREATE TRIGGER update_goods_num_purchase_insert
AFTER INSERT ON purchase
FOR EACH ROW
BEGIN
UPDATE goods
SET goods_num = goods_num + NEW.purchase_num
WHERE goods_id = NEW.goods_id;
END //
-- 插入销售表时自动更新商品信息
DROP TRIGGER IF EXISTS update_goods_num_sale_insert;
CREATE TRIGGER update_goods_num_sale_insert
AFTER INSERT ON sale
FOR EACH ROW
BEGIN
UPDATE goods
SET goods_num = goods_num - NEW.sale_num
WHERE goods_id = NEW.goods_id;
END //
-- 更新进货表时自动更新商品信息
DROP TRIGGER IF EXISTS update_goods_num_purchase_update;
CREATE TRIGGER update_goods_num_purchase_update
AFTER UPDATE ON purchase
FOR EACH ROW
BEGIN
UPDATE goods
SET goods_num = goods_num + (NEW.purchase_num - OLD.purchase_num)
WHERE goods_id = NEW.goods_id;
END //
-- 更新销售表时自动更新商品信息
DROP TRIGGER IF EXISTS update_goods_num_sale_update;
CREATE TRIGGER update_goods_num_sale_update
AFTER UPDATE ON sale
FOR EACH ROW
BEGIN
UPDATE goods
SET goods_num = goods_num - (NEW.sale_num - OLD.sale_num)
WHERE goods_id = NEW.goods_id;
END //
DELIMITER ;
系统主要功能模块设计
前端(Vue)—(网页设计)
1.首先需要构造一个登陆页面(login.vue),包含了账号密码登录的表单,通过用户名和密码进行登录验证。用户输入账号和密码后,点击登录按钮即可进行登录操作。登录成功后会跳转到主页。
2.然后设计一个主页面(Main.vue),其他页面按照此样式进行相应更改,并从此路由出去。
- 使用了 Element UI 组件库中的一些组件,如 el-container、el-header、el-menu
- 模板的结构是一个嵌套的布局,使用了 el-container 来创建一个容器,包含了头部(el-header)和内容区域(el-main)。头部中使用了 el-menu 组件来创建一个水平导航栏,其中包含了几个 el-menu-item 组件。这些菜单项通过 router-link 进行路由导航。
- 内容区域又分为两个部分:el-aside 和 el-main。el-aside 是一个垂直导航栏,使用了 el-menu 组件和 el-menu-item 组件,同样通过 router-link 进行路由导航。el-main 则是用来显示子路由的组件。
3.设置前端路由,定义不同路径对应的视图组件,从而实现页面间的切换和导航功能
(1)定义以下视图组件(分别对应不同页面):
- Main:主页面视图组件
- Login:登录页面视图组件
- Purchase:采购页面视图组件
- Client:客户页面视图组件
- Commodity:商品页面视图组件
- Sale:销售页面视图组件
- Staff:员工页面视图组件
(2)定义了路由规则(routes):
- ’/’ 路径对应登录页面组件 Login
-
‘/main’ 路径对应主页面组件 Main,并且定义了子路由(children):
- 子路由包括 ‘/purchase’、’/staff’、’/client’、’/commodity’、’/sale’,分别对应采购、员工、客户、商品、销售等页面组件。
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
28
29
30
31
32
33
34
35
36
37
38
39
40
import Vue from 'vue'
import VueRouter from 'vue-router'
import Main from '../views/Main.vue'
import Purchase from '../views/purchase.vue'
import Client from '../views/Client.vue'
import Commodity from '../views/Commodity.vue'
import Login from '../views/login.vue'
import Sale from '../views/sale.vue'
import Staff from '../views/staff.vue'
import Home from '../views/Home.vue'
Vue.use(VueRouter)
//添加组件(跳转页面)
const routes = [
{
path: '/',
component: Login,
},
{
path: '/main',
component: Main,
children:[
//子路由
{ path: '/purchase', component: Purchase},
{ path: '/staff', component: Staff},
{ path: '/client', component: Client},
{ path: '/commodity', component: Commodity},
{ path: '/sale', component: Sale}
]
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
4.各个页面的具体设计
- purchase.vue(进货信息表)
- Client.vue(客户表)
- Commodity.vue(商品信息表)
- sale.vue(销售信息表)
- staff.vue(员工表)
下面以Commodity.vue为例(具体代码见最后代码附录部分)
- 标题为“商品信息表”的
<h1>
标签。 - 如果存在商品数量低于10个的情况,则会显示一个警告框,用
<el-alert>
实现。 - 搜索框:用户可以输入关键字来搜索表格中的数据,用
<el-input>
实现。 - 表格区域:用
<el-table>
实现,包含四列,分别为商品编号、商品名称、商品数量和操作。其中,商品数量一列根据具体数据来判断是否需要标红,使用了<template>
和<span>
。 - 修改、添加记录方框:在操作列中,用户可以点击“编辑”或“删除”按钮进行对应的操作。如果是“编辑”,则会弹出一个对话框,用
<el-dialog>
实现。对话框中包含三个输入框:商品编号、商品名称和商品数量。如果是“添加”,则同样会弹出对话框,但是不需要传入已有的数据。 - 分页器:表格下方包含一个分页器,用
<el-pagination>
实现。 - 弹出窗:用于编辑或添加记录时使用,包含商品编号、商品名称和商品数量三个输入框,用
<el-form>
实现。同时还包含一个“重置”按钮和一个“确定”按钮,分别用于重置输入框和提交表单。 data
中定义了table
数组,用于存储从后端获取到的商品信息数据。created
方法会在组件创建时自动执行,调用init
方法来从后端获取商品信息数据并初始化表格。add
方法会在用户点击“添加一条记录”按钮时执行,用于弹出一个对话框。handEdit
方法会在用户点击“编辑”按钮时执行,用于弹出一个对话框并将选中行的数据传入对话框。handDelete
方法会在用户点击“删除”按钮时执行,用于发送请求删除选中行的数据。handleClose
方法会在对话框关闭时执行,用于重新初始化表格数据。reset
方法会在用户点击“重置”按钮时执行,用于重置表单输入框。save
方法会在用户点击“确定”按钮时执行,用于提交表单数据。tables
计算属性用于实现搜索功能,根据搜索框中的关键字过滤表格数据。hasInsufficientQuantity
计算属性用于判断是否存在商品数量低于10个的情况,以便显示警告框。
后端(Python)
1.连接MySQL数据库并执行相关操作。db.py
文件封装了连接数据库、查询数据、执行单条和多条 SQL 语句以及关闭连接的功能,并提供了一些常用的方法来操作数据库
具体解释如下:
DB_CONFIG
是一个字典,包含了连接数据库所需的配置信息,包括主机名、端口号、用户名、密码、数据库名和字符集。SQLManager
类是数据库操作类的定义,包含了一系列方法来执行数据库操作。__init__
方法是类的初始化方法,在创建类的实例时会自动调用。它初始化了连接对象conn
和游标对象cursor
,并调用connect
方法建立与数据库的连接。connect
方法用于连接数据库,它使用pymysql
模块的connect
函数来建立连接,并指定数据库的相关配置参数。然后获取游标对象cursor
,用于执行 SQL 语句。get_list
方法用于查询多条数据,接收一个 SQL 语句和参数,并使用游标对象的execute
方法执行 SQL 语句。然后使用fetchall
方法获取所有查询结果,并返回。get_one
方法用于查询单条数据,与get_list
方法类似,但是使用fetchone
方法获取单条查询结果。modify
方法用于执行单条 SQL 语句,接收一个 SQL 语句和参数。它使用游标对象的execute
方法执行 SQL 语句,并使用commit
方法提交事务。最后返回执行结果,如果受影响的行数大于0,则返回True
,否则返回False
。multi_modify
方法用于执行多条 SQL 语句,与modify
方法类似,但是使用executemany
方法执行多条 SQL 语句,并返回执行结果。close
方法用于关闭数据库连接和游标对象,分别调用cursor
对象的close
方法和conn
对象的close
方法。
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# 数据库操作类
import pymysql
DB_CONFIG = {
"host": "127.0.0.1",
"port": 3306,
"user": "root",
"passwd": "123456",
"db": "test1",
"charset": "utf8"
}
class SQLManager(object):
# 初始化实例方法
def __init__(self):
self.conn = None # 连接对象,建立与MySQL数据库的连接
self.cursor = None # 游标对象,主要负责执行SQL语句
self.connect()
# 连接数据库
def connect(self):
# 建立连接
self.conn = pymysql.connect(
host=DB_CONFIG["host"],
port=DB_CONFIG["port"],
user=DB_CONFIG["user"],
passwd=DB_CONFIG["passwd"],
db=DB_CONFIG["db"],
charset=DB_CONFIG["charset"]
)
# 获取游标对象;pymysql.cursors.DictCursor指定返回的结果类型为字典,默认是元组类型
# 在连接没有关闭之前,游标对象可以反复使用
self.cursor = self.conn.cursor(cursor=pymysql.cursors.DictCursor)
# 查询多条数据
def get_list(self, sql, args=None):
self.cursor.execute(sql, args) # 执行SQL语句,返回受影响的行数
return self.cursor.fetchall() # 返回所有查询结果
# 查询单条数据
def get_one(self, sql, args=None):
self.cursor.execute(sql, args)
return self.cursor.fetchone() # 返回单条查询结果
# 执行单条SQL语句
def modify(self, sql, args=None):
row = self.cursor.execute(sql, args)
self.conn.commit() # 对数据库的增、删、改操作需要提交事务,否则操作不生效
return row > 0 # 若影响的行数>0,说明执行成功,返回True;否则返回False
# 执行多条SQL语句
def multi_modify(self, sql, args=None):
rows = self.cursor.executemany(sql, args)
self.conn.commit()
return rows > 0
# 关闭数据库cursor和连接
def close(self):
self.cursor.close()
self.conn.close()
2.与前端建立连接。app.py
使用 Flask 框架搭建了一个简单的后端接口,提供了查询、添加、修改和删除客户信息的功能。它通过自定义的 JsonFlask
类处理返回 JSON 格式的响应,使用 SQLManager
类进行数据库操作,并使用 Flask-CORS 扩展解决跨域请求的问题。
下面以对client表的操作为例:
app = JsonFlask(__name__)
: 创建一个应用实例app
,使用自定义的JsonFlask
类,该类继承自 Flask 的Flask
类,用于处理返回 JSON 格式的响应。CORS(app, supports_credentials=True)
: 调用CORS
函数并传入应用实例app
,用于解决跨域请求,允许跨域请求携带凭证。db = SQLManager()
: 创建一个数据库连接对象db
,使用自定义的SQLManager
类。@app.route("/all_client", methods=["GET"])
: 使用装饰器@app.route
绑定路由/all_client
和请求方法GET
,定义了一个名为all_client
的视图函数。def all_client()
: 定义了视图函数all_client
,用于处理/all_client
路由的 GET 请求。它调用数据库连接对象db
的get_list
方法执行 SQL 查询语句,然后通过JsonResponse.success
方法构造成功的响应数据并返回。- 类似地,
@app.route
装饰器定义了/add_client
、/update_client
和/delete_client
等路由对应的视图函数,并在这些函数中执行相应的数据库操作,最后构造响应数据并返回。
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 创建视图应用,使用改造后的JsonFlask对象
app = JsonFlask(__name__)
# 解决跨域
CORS(app, supports_credentials=True)
# 数据库连接对象
db = SQLManager()
# 编写视图函数,绑定路由
# client表
@app.route("/all_client", methods=["GET"]) # 查询(全部)
def all_client():
result = db.get_list(sql='select * from client')
return JsonResponse.success(msg='查询成功', data=result)
@app.route("/add_client", methods=["POST"]) # 添加(单个)
def add_client():
data = json.loads(request.data) # 将json字符串转为dict
isOk = db.modify(sql='insert into client(client_id,client_name,phone_number,address) values(%s,%s,%s,%s)',
args=[data['client_id'], data['client_name'], data['phone_number'], data['address']])
# python三元表达式
return JsonResponse.success(msg='添加成功') if isOk else JsonResponse.fail(msg='添加失败')
@app.route("/update_client", methods=["PUT"]) # 修改(单个)
def update_client():
# request.data获取请求体数据
# 前端在发送请求时,由于指定了Content-Type为application/json;故request.data获取到的就是json数据
data = json.loads(request.data) # 将json字符串转为dict
if 'client_id' not in data: # 改为form里对应的xx_id
return JsonResponse.fail(msg='需要传入client_id')
isOk = db.modify(sql='update client set client_name=%s,phone_number=%s,address=%s where client_id=%s', # 改为
args=[data['client_name'], data['phone_number'], data['address'], data['client_id']])
return JsonResponse.success(msg='修改成功') if isOk else JsonResponse.fail(msg='修改失败')
@app.route("/delete_client", methods=["DELETE"]) # 删除(单个)
def delete_client():
# request.args获取请求链接中 ? 后面的所有参数;以字典的方式存储
if 'client_id' not in request.args:
return JsonResponse.fail(msg='需要传入client_id')
isOk = db.modify(sql='delete from client where client_id=%s', args=[request.args['client_id']])
return JsonResponse.success(msg='删除成功') if isOk else JsonResponse.fail(msg='删除失败')
各模块的主要算法对应的原代码
见code文件夹
四、调试与运行结果及存在的主要问题
调试、运行和存在的问题和解决方案
postgresql难以连接
python连接postgresql的库psycopg2无法返回字典,即使使用:
1
self.cursor = self.conn.cursor(cursor_factory=extras.DictCursor)
返回的仍然是列表,不便于使用。查询资料后发现没有解决办法,所以改用mysql,详情见下面网站:
前端的交互无响应
解决:使用console.log(“打印文本”)和浏览器中按F12进入开发者模式调试。
举例:修改弹出警告样式,一开始是默认使用库中的类,通过开发者模式发现问题,并重写该类(使用! important
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
.el-alert {
width: 80% ! important;
padding: 8px 16px;
margin: 0;
box-sizing: border-box;
border-radius: 4px;
position: relative;
background-color: #FFF;
overflow: hidden;
opacity: 1;
display: flex;
align-items: center;
transition: opacity .2s;
}
不知道是否连接上正确的数据库
解决:使用以下代码尝试看是否能访问正确的数据
1
2
3
4
5
from db import *
db = SQLManager()
# result = db.get_list(sql="select * from public.user;")
result = db.get_list(sql="select * from sale")
print(result)
通过后端的接收信息来判断前端的操作是否被正确执行:
五、 完成结果视频展示
为了方便展示,销售流水号设置为saX,商品编号设置为gX,员工编号设置为sX,进货编号设置为pX,客户编号设置为cX。
登陆->依次展示各个表格->以进货过程为例,观察表格的插入、修改、删除以及对其他表格的影响。
在员工信息表:
-
展示查找和排序:
-
展示修改和重置:
-
展示删除:
进入商品信息表:
- 插入一个新表项(数量设置为0) -> 进入进货信息表,创造表项(商品编号为前面创造的商品),注意外键约束(staff_id和goods_id必须是员工表和商品表中存在的-> 回到商品信息表,发现刚创建的商品数量发生变化。(并且进货金额即使填错了,也有纠正机制将其记录正确)
- 进入销售信息表,创造表项,注意外键约束 -> 回到商品信息表,发现商品预警。
- 进入进货信息表增加,回到商品信息表,发现商品预警消失
演示视频(点击即可播放,视频加载时间可能需要较久,请耐心等待):
六、课程设计小结
总结
本次数据库实验我们成功开发一个超市库存管理系统,包括商品信息管理、进货管理、销售管理和库存管理等功能。为了实现这个目标,我们选择了以下技术和工具:PyCharm作为Python的集成开发环境(IDE),DataGrip作为数据库集成开发环境(IDE),MySQL作为后台数据库,Node.js和Vue.js作为前台开发工具,以及Visual Studio Code(VS Code)用于编写前端代码。
在实验中,我们按照要求逐步完成了超市库存管理系统的设计和开发。首先,我们针对给定的系统进行需求分析,设计了系统结构图和系统功能模块图。通过这些图表,我们能够清晰地了解系统的整体架构和各个功能模块之间的关系,为后续的设计和开发提供了指导。
接下来,我们进行了数据库概念模型的设计,使用E-R图表示系统的数据模型,并将其转换为满足3NF的关系模式。通过这一步骤,我们能够对系统的数据结构有一个清晰的把握,并且能够更好地进行数据库的设计和管理。同时,我们也考虑了系统的安全性和完整性,确保了对数据库的保护。
在开发过程中,我们使用了Vue.js作为前端框架,结合ElementUI组件库,实现了较为友好的用户界面。我们使用python与前端进行交互,并且连接MySQL数据库执行相关操作。
总的来说,本次数据库实验通过使用Python、MySQL、Node.js和Vue.js等工具和技术,成功设计和开发了一个超市库存管理系统。在实验过程中,我们深入理解了数据库的设计原理和开发方法,并且熟悉了常用的开发工具和框架。通过实践,我们提升了对数据库应用系统的理解和开发能力,为以后的项目实践打下了坚实的基础。
改进的地方
在这个数据库实验的设计和开发过程中,存在一些可以改进的地方:
- 前端界面交互体验优化:虽然使用了Vue.js和ElementUI构建了用户界面,但可以进一步优化用户交互体验,例如增加动画效果。
- 安全性和完整性进一步加强:在数据库层面,可以考虑加强数据的安全性,加密敏感信息,例如员工和客户的个人信息。
- 系统性能优化:随着数据量的增加,系统性能可能成为一个问题。可以考虑对数据库的索引、查询语句以及前端页面加载等方面进行优化,以提高系统的响应速度和并发处理能力。
- 跨平台兼容性:考虑到不同操作系统和浏览器的兼容性,可以进一步测试和优化系统在不同平台上的表现。
- 代码质量和可维护性:在开发过程中,还可以提高代码的可复用性和可维护性,例如后端处理操作时的代码,以便于后续的维护和扩展。
- 本实验均在本地开发环境完成,并没有部署到服务器上。