数据库实验

1、opengauss数据库环境安装

根据文档安装即可,不过我使用的是docker,

镜像为enmotech/opengauss:latest,然后配合dbeaver使用

2、opengauss场景化综合应用实验

不是很难,但是步骤很多,很烦

首先要创建数据库,这个就不多说了,使用gui创建或者sql命令都行

创建client表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- 如果存在 client 表,则先删除它
DROP TABLE IF EXISTS client;

-- 创建 client 表
CREATE TABLE client (
c_id INTEGER PRIMARY KEY,
c_name VARCHAR(100) NOT NULL,
c_mail CHAR(30) UNIQUE,
c_id_card CHAR(20) UNIQUE NOT NULL,
c_phone CHAR(20) UNIQUE NOT NULL,
c_password CHAR(20) NOT NULL
);

-- 为字段添加注释
COMMENT ON COLUMN client.c_id IS '客户编码';
COMMENT ON COLUMN client.c_name IS '客户名称';
COMMENT ON COLUMN client.c_mail IS '客户邮箱';
COMMENT ON COLUMN client.c_id_card IS '客户身份证';
COMMENT ON COLUMN client.c_phone IS '客户手机号';
COMMENT ON COLUMN client.c_password IS '客户登录密码';

创建band_card表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-- 如果存在 bank_card 表,则先删除它
DROP TABLE IF EXISTS bank_card;

-- 创建 bank_card 表
CREATE TABLE bank_card (
b_number CHAR(30) PRIMARY KEY, -- 银行卡号,作为主键
b_type CHAR(20), -- 银行卡类型
b_c_id INTEGER NOT NULL, -- 所属客户编号,不能为空
FOREIGN KEY (b_c_id) REFERENCES client(c_id) -- 外键,引用 client 表的 c_id 字段
);

-- 为字段添加注释
COMMENT ON COLUMN bank_card.b_number IS '银行卡号';
COMMENT ON COLUMN bank_card.b_type IS '银行卡类型';
COMMENT ON COLUMN bank_card.b_c_id IS '所属客户编号';

创建finances_product表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- 如果存在 finances_product 表,则先删除它
DROP TABLE IF EXISTS finances_product;

-- 创建 finances_product 表
CREATE TABLE finances_product (
p_name VARCHAR(100) NOT NULL, -- 产品名称
p_id INTEGER PRIMARY KEY, -- 产品编号,作为主键
p_description VARCHAR(4000), -- 产品描述
p_amount INTEGER, -- 购买金额
p_year INTEGER -- 理财年限
);

-- 为字段添加注释
COMMENT ON COLUMN finances_product.p_name IS '产品名称';
COMMENT ON COLUMN finances_product.p_id IS '产品编号';
COMMENT ON COLUMN finances_product.p_description IS '产品描述';
COMMENT ON COLUMN finances_product.p_amount IS '购买金额';
COMMENT ON COLUMN finances_product.p_year IS '理财年限';

创建insurance表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- 如果存在 insurance 表,则先删除它
DROP TABLE IF EXISTS insurance;

-- 创建 insurance 表
CREATE TABLE insurance (
i_name VARCHAR(100) NOT NULL, -- 保险名称
i_id INTEGER PRIMARY KEY, -- 保险编号,作为主键
i_amount INTEGER, -- 保险金额
i_person CHAR(20), -- 适用人群
i_year INTEGER, -- 保险年限
i_project VARCHAR(200) -- 保障项目
);

-- 为字段添加注释
COMMENT ON COLUMN insurance.i_name IS '保险名称';
COMMENT ON COLUMN insurance.i_id IS '保险编号';
COMMENT ON COLUMN insurance.i_amount IS '保险金额';
COMMENT ON COLUMN insurance.i_person IS '适用人群';
COMMENT ON COLUMN insurance.i_year IS '保险年限';
COMMENT ON COLUMN insurance.i_project IS '保障项目';

创建fund表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- 如果存在 fund 表,则先删除它
DROP TABLE IF EXISTS fund;

-- 创建 fund 表
CREATE TABLE fund (
f_name VARCHAR(100) NOT NULL, -- 基金名称
f_id INTEGER PRIMARY KEY, -- 基金编号,作为主键
f_type CHAR(20), -- 基金类型
f_amount INTEGER, -- 基金金额
risk_level CHAR(20) NOT NULL, -- 风险等级
f_manager INTEGER NOT NULL -- 基金管理者
);

-- 为字段添加注释
COMMENT ON COLUMN fund.f_name IS '基金名称';
COMMENT ON COLUMN fund.f_id IS '基金编号';
COMMENT ON COLUMN fund.f_type IS '基金类型';
COMMENT ON COLUMN fund.f_amount IS '基金金额';
COMMENT ON COLUMN fund.risk_level IS '风险等级';
COMMENT ON COLUMN fund.f_manager IS '基金管理者';

创建property表

property表中由于需要使用pro_type对pro_pif_id进行引用表的判断,因此没有在创建表时就定义好引用。

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
-- 如果存在 property 表,则先删除它
DROP TABLE IF EXISTS property;

-- 创建 property 表
CREATE TABLE property (
pro_id INTEGER PRIMARY KEY, -- 资产编号,作为主键
pro_c_id VARCHAR(100) NOT NULL, -- 客户编号,不能为空
pro_pif_id INTEGER NOT NULL, -- 商品编号,不能为空
pro_type INTEGER NOT NULL CHECK (pro_type IN (1, 2, 3)), -- 商品类型,1表示理财产品;2表示保险;3表示基金
pro_status CHAR(20), -- 商品状态
pro_quantity INTEGER, -- 商品数量
pro_income INTEGER, -- 商品收益
pro_purchase_time DATE, -- 购买时间
FOREIGN KEY (pro_c_id) REFERENCES client(c_id) -- 外键,引用 client 表的 c_id 字段
-- 不在这里定义 pro_pif_id 的外键约束
);

-- 为字段添加注释
COMMENT ON COLUMN property.pro_id IS '资产编号';
COMMENT ON COLUMN property.pro_c_id IS '客户编号';
COMMENT ON COLUMN property.pro_pif_id IS '商品编号';
COMMENT ON COLUMN property.pro_type IS '商品类型,1表示理财产品;2表示保险;3表示基金';
COMMENT ON COLUMN property.pro_status IS '商品状态';
COMMENT ON COLUMN property.pro_quantity IS '商品数量';
COMMENT ON COLUMN property.pro_income IS '商品收益';
COMMENT ON COLUMN property.pro_purchase_time IS '购买时间';

初始化client表

1
2
3
4
5
6
7
8
9
10
11
INSERT INTO client (c_id, c_name, c_mail, c_id_card, c_phone, c_password) VALUES
(1, '张三', 'zhangsan@example.com', '123456789012345678', '13800000001', 'password1'),
(2, '李四', 'lisi@example.com', '123456789012345679', '13800000002', 'password2'),
(3, '王五', 'wangwu@example.com', '123456789012345680', '13800000003', 'password3'),
(4, '赵六', 'zhaoliu@example.com', '123456789012345681', '13800000004', 'password4'),
(5, '钱七', 'qianqi@example.com', '123456789012345682', '13800000005', 'password5'),
(6, '孙八', 'sunba@example.com', '123456789012345683', '13800000006', 'password6'),
(7, '周九', 'zhoujiu@example.com', '123456789012345684', '13800000007', 'password7'),
(8, '吴十', 'wushi@example.com', '123456789012345685', '13800000008', 'password8'),
(9, '郑十一', 'zheng11@example.com', '123456789012345686', '13800000009', 'password9'),
(10, '冯十二', 'feng12@example.com', '123456789012345687', '13800000010', 'password10');

初始化bank_card表

1
2
3
4
5
6
7
8
9
10
11
INSERT INTO bank_card (b_number, b_type, b_c_id) VALUES
('6222021001000001', '储蓄卡', 1),
('6222021001000002', '信用卡', 2),
('6222021001000003', '储蓄卡', 3),
('6222021001000004', '信用卡', 4),
('6222021001000005', '储蓄卡', 5),
('6222021001000006', '储蓄卡', 6),
('6222021001000007', '信用卡', 7),
('6222021001000008', '储蓄卡', 8),
('6222021001000009', '信用卡', 9),
('6222021001000010', '储蓄卡', 10);

初始化finances_product表

1
2
3
4
5
6
7
8
9
10
11
INSERT INTO finances_product (p_name, p_id, p_description, p_amount, p_year) VALUES
('稳健型理财1号', 1, '适合低风险投资者的稳健型理财产品,年化收益约为3%。', 10000, 1),
('稳健型理财2号', 2, '低风险短期理财产品,资金灵活流动,年化收益约为3.5%。', 20000, 2),
('进取型理财1号', 3, '适合中风险投资者的理财产品,年化收益约为5%。', 30000, 3),
('进取型理财2号', 4, '中风险中收益产品,资金适中,年化收益约为5.5%。', 50000, 3),
('高收益型理财1号', 5, '高风险高收益产品,适合追求高回报的投资者,年化收益约为7%。', 100000, 5),
('高收益型理财2号', 6, '高风险理财产品,投资期长,年化收益可达8%。', 150000, 5),
('保本型理财1号', 7, '低风险保本产品,资金安全有保障,年化收益约为2.5%。', 5000, 1),
('保本型理财2号', 8, '中长期保本型理财产品,适合稳健投资者,年化收益约为3%。', 10000, 3),
('灵活型理财1号', 9, '灵活投资型理财产品,可随时赎回,年化收益约为4%。', 20000, 2),
('灵活型理财2号', 10, '灵活资金管理产品,短期收益高,年化收益约为4.5%。', 30000, 1);

初始化insurance表

1
2
3
4
5
6
7
8
9
10
11
INSERT INTO insurance (i_name, i_id, i_amount, i_person, i_year, i_project) VALUES
('基础医疗保险', 1, 5000, '个人', 1, '住院报销、门诊费用报销'),
('全面健康险', 2, 20000, '个人', 5, '重大疾病保障、住院费用报销'),
('家庭综合险', 3, 10000, '家庭', 3, '意外伤害、财产损失保障'),
('老年健康保险', 4, 15000, '老年人', 10, '慢性病费用报销、住院补贴'),
('儿童成长保险', 5, 8000, '儿童', 5, '教育金、意外医疗保障'),
('高端医疗保险', 6, 50000, '个人', 3, '高端医疗服务、海外医疗保障'),
('意外伤害保险', 7, 10000, '个人', 1, '意外医疗、意外身故赔偿'),
('女性专属保险', 8, 12000, '女性', 5, '健康保障、重大疾病保障'),
('留学生保险', 9, 15000, '留学生', 2, '海外医疗、紧急救援服务'),
('企业员工团体险', 10, 100000, '企业员工', 1, '意外伤害、医疗报销');

初始化fund表

1
2
3
4
5
6
7
8
9
10
11
INSERT INTO fund (f_name, f_id, f_type, f_amount, risk_level, f_manager) VALUES
('稳健收益基金A', 1, '债券型', 10000, '低风险', 101),
('稳健收益基金B', 2, '债券型', 20000, '低风险', 102),
('成长型基金A', 3, '股票型', 30000, '中风险', 103),
('成长型基金B', 4, '股票型', 40000, '中风险', 104),
('高收益基金A', 5, '混合型', 50000, '高风险', 105),
('高收益基金B', 6, '混合型', 60000, '高风险', 106),
('指数基金A', 7, '指数型', 25000, '中风险', 107),
('指数基金B', 8, '指数型', 35000, '中风险', 108),
('保本基金A', 9, '保本型', 15000, '低风险', 109),
('保本基金B', 10, '保本型', 18000, '低风险', 110);

初始化property表

1
2
3
4
5
6
7
8
9
10
11
INSERT INTO property (pro_id, pro_c_id, pro_pif_id, pro_type, pro_status, pro_quantity, pro_income, pro_purchase_time) VALUES
(1, 1, 1, 1, '持有中', 10, 5000, '2024-01-10'), -- 理财产品,引用 finances_product 表
(2, 2, 2, 1, '持有中', 5, 3000, '2024-02-15'), -- 理财产品,引用 finances_product 表
(3, 3, 3, 2, '已赔付', 1, 1500, '2024-03-20'), -- 保险,引用 insurance 表
(4, 4, 4, 2, '保单生效', 1, 0, '2024-04-05'), -- 保险,引用 insurance 表
(5, 5, 5, 3, '已赎回', 20, 8000, '2024-05-10'), -- 基金,引用 fund 表
(6, 6, 6, 3, '持有中', 10, 7000, '2024-06-18'), -- 基金,引用 fund 表
(7, 7, 7, 1, '持有中', 2, 2000, '2024-07-01'), -- 理财产品,引用 finances_product 表
(8, 8, 8, 2, '保单生效', 1, 0, '2024-08-25'), -- 保险,引用 insurance 表
(9, 9, 9, 3, '已赎回', 15, 5000, '2024-09-12'), -- 基金,引用 fund 表
(10, 10, 10, 1, '持有中', 5, 2500, '2024-10-08'); -- 理财产品,引用 finances_product 表

添加约束

1
2
3
ALTER table finances_product ADD CONSTRAINT c_p_mount CHECK (p_amount >=0);
ALTER table fund ADD CONSTRAINT c_f_mount CHECK (f_amount >=0);
ALTER table insurance ADD CONSTRAINT c_i_mount CHECK (i_amount >=0);

查询部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
select * from bank_card bc ;
select * from property where pro_status = '持有中';
select count(*) from client;
select count(*) from bank_card where b_type = '储蓄卡';
select count(*) from bank_card where b_type = '信用卡';
select avg(i_amount) from insurance;
select i_name,i_amount from insurance where i_amount = (select max(i_amount) from insurance);
select i_name,i_amount from insurance where i_amount = (select min(i_amount) from insurance);
select c_id, c_name, c_id_card from client where exists (select 1 from bank_card where bank_card.b_c_id = client.c_id);
select c_id, c_name, c_id_card from client where not exists (select 1 from bank_card where bank_card.b_c_id = client.c_id and bank_card.b_number like '622202130202000001%');
select i_name,i_person from insurance where i_amount > (select avg(i_amount) from insurance);
select i_id,i_name,i_amount,i_person from insurance where i_id >2 order by i_amount desc;
select p_year,count(*) from finances_product group by p_year;
select i_person,count(*) from insurance group by i_person having count(*) = 2;
with fundinfo as (select f_id,f_name,f_amount,risk_level from fund)
select * from fundinfo;

md,全是截图,截得快吐了

视图部分

1
2
3
4
5
6
7
8
9
10
create view v_client as select c.c_id,c.c_name,c.c_id_card from "client" c 
where exists (select 1 from bank_card b where b.b_c_id = c.c_id);
select * from v_client;
create or replace view v_client as select c.c_id,c.c_name,c.c_id_card from "client" c join bank_card b on
b.b_c_id = c.c_id where b.b_type='信用卡';
select * from v_client;
alter view v_client rename to v_client_next;
select * from v_client_next;
drop view v_client_next;
select * from v_client_next vcn ;

索引部分

1
2
3
4
create index idx_property_pro_c_id on property(pro_c_id);
reindex index idx_property_pro_c_id;
alter index idx_property_pro_c_id rename to idx_property_temp;
drop index idx_property_temp;

更新与删除部分

1
2
update bank_card set b_type = '借记卡' from "client" where bank_card.b_c_id = "client".c_id and bank_card.b_c_id <10
delete from fund where f_id<3

3、openGauss自选题应用实验

选题:题库系统

构建数据库

1
2
create database examsystem;
create schema exam;

构建基本表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 构建用户表
create table users (
user_id serial primary key, -- 用户ID, 自动递增
username varchar(100) not null, -- 用户名
password varchar(255) not null, -- 用户密码,存储加密后的密码
email varchar(255), -- 用户邮箱
role varchar(20) not null, -- 用户角色,使用 varchar 类型
created_at timestamp default current_timestamp, -- 用户创建时间
updated_at timestamp default current_timestamp, -- 用户更新时间
check (role in ('student', 'teacher', 'admin')) -- 使用 check 约束限制角色值
);

-- 添加注释
comment on column users.user_id is '用户唯一标识符,自动递增';
comment on column users.username is '用户登录名,必须唯一';
comment on column users.password is '用户密码,存储加密后的密码';
comment on column users.email is '用户的电子邮件地址';
comment on column users.role is '用户角色,值为 student、teacher 或 admin';
comment on column users.created_at is '记录创建时间,默认当前时间';
comment on column users.updated_at is '记录最后更新时间,默认当前时间';
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 构建题目表
create table questions (
question_id serial primary key, -- 题目ID, 自动递增
content text not null, -- 题目内容
type varchar(20) not null, -- 题目类型
difficulty int not null, -- 难度等级 (1-5)
category_id int, -- 分类ID (外键)
created_at timestamp default current_timestamp, -- 创建时间
updated_at timestamp default current_timestamp, -- 更新时间
check (type in ('single', 'multiple', 'fill', 'true_false')), -- 题目类型约束
check (difficulty between 1 and 5) -- 难度等级约束 (1-5)
);

-- 添加注释
comment on column questions.question_id is '题目唯一标识符,自动递增';
comment on column questions.content is '题目的内容';
comment on column questions.type is '题目类型,值为 single, multiple, fill 或 true_false';
comment on column questions.difficulty is '题目的难度等级,范围为 1 到 5';
comment on column questions.category_id is '题目所属的分类ID';
comment on column questions.created_at is '题目创建时间,默认当前时间';
comment on column questions.updated_at is '题目更新时间,默认当前时间';

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 构建选项表
create table options (
option_id serial primary key, -- 选项ID, 自动递增
question_id int not null, -- 题目ID (外键)
content varchar(255) not null, -- 选项内容
is_correct boolean not null, -- 是否为正确选项
foreign key (question_id) references questions(question_id) on delete cascade -- 外键约束
);

-- 添加注释
comment on column options.option_id is '选项唯一标识符,自动递增';
comment on column options.question_id is '关联的题目ID';
comment on column options.content is '选项的内容';
comment on column options.is_correct is '选项是否为正确答案';

1
2
3
4
5
6
7
8
9
10
11
12
# 构建题目分类表
create table categories (
category_id serial primary key, -- 分类ID, 自动递增
name varchar(100) not null, -- 分类名称
description text -- 分类描述
);

-- 添加注释
comment on column categories.category_id is '分类唯一标识符,自动递增';
comment on column categories.name is '分类名称';
comment on column categories.description is '分类描述';

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 构建答案表
create table answers (
answer_id serial primary key, -- 答案ID, 自动递增
user_id int not null, -- 用户ID (外键)
question_id int not null, -- 题目ID (外键)
answer text not null, -- 用户的答案内容
is_correct boolean, -- 用户的答案是否正确
submitted_at timestamp default current_timestamp, -- 提交时间
foreign key (user_id) references users(user_id) on delete cascade, -- 外键约束
foreign key (question_id) references questions(question_id) on delete cascade -- 外键约束
);

-- 添加注释
comment on column answers.answer_id is '答案唯一标识符,自动递增';
comment on column answers.user_id is '提交答案的用户ID';
comment on column answers.question_id is '关联的题目ID';
comment on column answers.answer is '用户的答案内容';
comment on column answers.is_correct is '用户答案是否正确';
comment on column answers.submitted_at is '答案提交时间,默认当前时间';

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 构建考试表
create table exams (
exam_id serial primary key, -- 考试ID, 自动递增
name varchar(255) not null, -- 考试名称
start_time timestamp not null, -- 考试开始时间
end_time timestamp not null, -- 考试结束时间
duration int not null, -- 考试时长(分钟)
created_at timestamp default current_timestamp -- 创建时间
);

-- 添加注释
comment on column exams.exam_id is '考试唯一标识符,自动递增';
comment on column exams.name is '考试名称';
comment on column exams.start_time is '考试开始时间';
comment on column exams.end_time is '考试结束时间';
comment on column exams.duration is '考试时长,单位为分钟';
comment on column exams.created_at is '考试创建时间,默认当前时间';

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 构建考试关联表
create table exam_questions (
exam_id int not null, -- 考试ID (外键)
question_id int not null, -- 题目ID (外键)
question_order int not null, -- 题目在考试中的顺序
primary key (exam_id, question_id), -- 联合主键
foreign key (exam_id) references exams(exam_id) on delete cascade, -- 外键约束
foreign key (question_id) references questions(question_id) on delete cascade -- 外键约束
);

-- 添加注释
comment on column exam_questions.exam_id is '关联的考试ID';
comment on column exam_questions.question_id is '关联的题目ID';
comment on column exam_questions.question_order is '题目在考试中的顺序';

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 构建成绩表
create table scores (
score_id serial primary key, -- 成绩ID, 自动递增
user_id int not null, -- 用户ID (外键)
exam_id int not null, -- 考试ID (外键)
score decimal(5,2) not null, -- 得分
total_score decimal(5,2) not null, -- 总分
created_at timestamp default current_timestamp, -- 记录时间
foreign key (user_id) references users(user_id) on delete cascade, -- 外键约束
foreign key (exam_id) references exams(exam_id) on delete cascade -- 外键约束
);

-- 添加注释
comment on column scores.score_id is '成绩唯一标识符,自动递增';
comment on column scores.user_id is '用户ID';
comment on column scores.exam_id is '考试ID';
comment on column scores.score is '用户在考试中的得分';
comment on column scores.total_score is '考试的总分';
comment on column scores.created_at is '成绩记录时间,默认当前时间';
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 日志表
create table logs (
log_id serial primary key, -- 日志ID, 自动递增
user_id int not null, -- 用户ID (外键)
action varchar(255) not null, -- 操作类型(如登录、提交考试等)
description text, -- 操作描述
created_at timestamp default current_timestamp, -- 操作时间
foreign key (user_id) references users(user_id) on delete cascade -- 外键约束
);

-- 添加注释
comment on column logs.log_id is '日志唯一标识符,自动递增';
comment on column logs.user_id is '执行操作的用户ID';
comment on column logs.action is '操作类型(如登录、提交考试等)';
comment on column logs.description is '操作的详细描述';
comment on column logs.created_at is '操作时间,默认当前时间';

1
2
# 为题目表添加外键
alter table questions add constraint fk_category foreign key (category_id) references categories(category_id) on delete set null;

插入数据部分

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
# 使用faker库生成随机数据的sql
from faker import Faker
import random

# 初始化 Faker
fake = Faker()

# 定义角色列表
roles = ['student']

# 打开文件写入生成的 SQL(可选)
with open("insert_users.sql", "w") as file:
print("-- Insert users into the users table", file=file)

# 生成 10 个用户
for i in range(1, 11):
username = f"user{i}" # 用户名格式
password = fake.password(length=10) # 随机生成密码
email = fake.email() # 随机生成邮箱
role = random.choice(roles) # 随机分配角色

# 生成插入语句
insert_statement = f"""insert into users (username, password, email, role) values ('{username}', '{password}', '{email}', '{role}');"""
print(insert_statement, file=file)
print(insert_statement)

1
2
3
4
5
6
7
8
9
10
11
12
# 随机生成十个用户插入用户表
insert into users (username, password, email, role) values ('user1', 'tW4B$^dM^4', 'kbeltran@example.net', 'student');
insert into users (username, password, email, role) values ('user2', '_4OL9PUsm8', 'gonzalezsarah@example.org', 'student');
insert into users (username, password, email, role) values ('user3', 'DS59L#Bh6^', 'sgarcia@example.com', 'student');
insert into users (username, password, email, role) values ('user4', 'Y*dzVcyN#7', 'morganbraun@example.com', 'student');
insert into users (username, password, email, role) values ('user5', 'E#8YQGcn@F', 'eric85@example.com', 'student');
insert into users (username, password, email, role) values ('user6', '78UXW1_Y*g', 'snyderelizabeth@example.org', 'student');
insert into users (username, password, email, role) values ('user7', '(7Jrps@!C2', 'brenda32@example.com', 'student');
insert into users (username, password, email, role) values ('user8', '6f)RmRu7&L', 'robertwiley@example.com', 'student');
insert into users (username, password, email, role) values ('user9', 'Bv1HXw2Dj@', 'rwalter@example.org', 'student');
insert into users (username, password, email, role) values ('user10', 'Y7fKMBDp(1', 'timothy67@example.net', 'student');

1
2
3
4
5
6
7
8
9
10
11
12
13
# 随机生成十个分类插入题目分类表
insert into categories (name, description) values
('数学', '涉及基础代数、几何、概率与统计等内容的数学题目'),
('物理', '涵盖经典力学、电磁学、光学、热力学等物理领域的题目'),
('化学', '包含无机化学、有机化学以及物质结构等内容的题目'),
('生物', '涉及细胞生物学、遗传学和生态学等生物学领域'),
('计算机科学', '覆盖编程、数据结构、算法及计算机理论的题目'),
('历史', '包括世界历史、中国历史和重大历史事件分析'),
('地理', '涵盖自然地理、人文地理及地球科学相关内容'),
('文学', '涉及中外文学作品、文学史及文学体裁分析'),
('外语', '包含英语、法语等外语阅读、语法与写作的题目'),
('通识教育', '泛指综合性、跨学科的知识题目,用于扩展视野');

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
# 随机生成一百个题目(但是毫无实际意义)
from faker import Faker
import random

# 初始化 Faker
fake = Faker()

# 定义题目类型和难度范围
question_types = ['single', 'multiple', 'fill', 'true_false']
difficulty_levels = [1, 2, 3, 4, 5]
category_ids = list(range(1, 11)) # 假设分类表中有 10 个分类,ID 范围是 1-10

# 打开文件写入生成的 SQL(可选)
with open("insert_questions.sql", "w") as file:
print("-- Insert questions into the questions table", file=file)

# 生成 100 个问题
for i in range(1, 101):
content = fake.sentence(nb_words=10) # 随机生成 10 个单词组成的问题内容
question_type = random.choice(question_types) # 随机选择题目类型
difficulty = random.choice(difficulty_levels) # 随机选择难度等级
category_id = random.choice(category_ids) # 随机选择分类 ID

# 生成插入语句
insert_statement = f"""insert into questions (content, type, difficulty, category_id) values ('{content}', '{question_type}', {difficulty}, {category_id});"""
print(insert_statement, file=file)
print(insert_statement)

查询部分

1
2
3
4
5
6
7
8
9
10
# 单表查询
select * from users;
select username,email from users;
select * from users where role = 'student';
select username,length(username) as len from users;
select * from users where role in ('teacher','admin');
select * from users where username like 'user%';
select * from users order by email asc;
select count(*) as count from users;
select role,count(*) as rolecount from users group by role;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 多表查询
select q.content, c.name as category_name from questions q join categories c on q.category_id = c.category_id;
select q.content, c.name as category_name, u.username from questions q join categories c
on q.category_id = c.category_id join users u on q.category_id = u.user_id;
select c1.name as category1, c2.name as category2, c1.description from categories c1 join categories c2
on c1.description = c2.description and c1.category_id <> c2.category_id;
select c.name as category_name, q.content as question_content from categories c left join questions q
on c.category_id = q.category_id;
select username, (select count(*) from users) as total_users from users;
select name from categories c where exists (select 1 from questions q where q.category_id = c.category_id);
select username from users u where exists (select 1 from questions q where q.category_id = u.user_id);
select d.category_name, d.question_count from
(select c.name as category_name, count(q.content) as question_count
from categories c left join questions q on c.category_id = q.category_id group by c.name)
d order by d.question_count desc;

视图部分

1
2
3
4
# 创建视图
create view student_users as select username, email, role from users where role = 'student';
create view student_user_emails as select username, email from student_users;
select username from student_user_emails order by username asc;

触发器部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 创建触发器函数
create or replace function log_user_login()
returns trigger as $$
begin
insert into logs (user_id, action, created_at, description)
values (new.user_id, 'login', current_timestamp, null);
return new;
end;
$$ language plpgsql;
# 创建触发器
create trigger user_login
after update on users
for each row
when (new.updated_at is distinct from old.updated_at)
execute procedure log_user_login();
# 触发触发器
update users
set updated_at = current_timestamp
where role = 'admin';

注意opengauss创建触发器使用的是procedure而不是function

新建用户并给予权限

1
2
create user teacher with password 'teacher_password';
grant insert, update, delete on table questions to teacher;

4、华为 openGauss 数据库性能检查实验

由于镜像安装的openguass里面没有这个工具,虽然是企业版的,所以使用了pg_top+pgbench结合的方式来实现对数据库的性能检查

这两个工具都可以通过apt下载

1
2
3
4
5
6
7
# 先用pg_top连接 
pg_top -h 127.0.0.1 -p 5432 -U gaussdb -d pgbenchtest -W -s 2
# 先创建数据库(这个就不写,是提前创建好的),然后用pgbench生成测试数据
pgbench -i -s 10 -h 127.0.0.1 -p 5432 -U gaussdb pgbenchtest
# 开测!
pgbench -c 20 -j 5 -T 60 -h 127.0.0.1 -p 5432 -U gaussdb pgbenchtest
# 观察pg_top的变化,并且pgbench会有报告生成

5、使用JDBC连接openGauss数据库

https://opengauss.obs.cn-south-1.myhuaweicloud.com/1.0.0/arm/openGauss-1.0.0-JDBC.tar.gz

下载上面的驱动,然后用maven生成模板,添加依赖即可

复制下面的代码到maven的Main.java文件中,修改数据库配置即可成功连接

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
# Main.java
package org.example;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class Main {
public static void main(String[] args) {
// 数据库连接信息
String url = "jdbc:postgresql://192.168.92.188:5432/finance";
String user = "gaussdb";
String password = "Opengauss123!";

// 加载驱动程序并建立连接
try {
Class.forName("org.postgresql.Driver"); // OpenGauss 使用 PostgreSQL 的 JDBC 驱动
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println("连接成功!");

// 执行查询
String query = "SELECT version();"; // 查询数据库版本
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(query);

// 输出查询结果
while (rs.next()) {
System.out.println("数据库版本: " + rs.getString(1));
}

// 关闭资源
rs.close();
stmt.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

数据库课程考察与课程设计

由于课程考察和课程设计并不冲突,所以打算在一个项目中同时实现,课程考察是使用非关系型数据库,课程设计是使用关系型数据库

所以课程考察可以使用redis,然后课程设计使用opengauss(懒得配环境是这样的,而且可以重复利用之前用过的数据

后面用了mysql,因为opengauss没有python的兼容

开发顺序:先开发前后端界面再开发数据库,然后连接数据库将数据库中的内容输出到前端。

1、开发前后端界面

由于只是个课设,所以没有前后端分离的打算,那样比较麻烦,所以打算使用python的flask+jinja2来同时实现前后端,即使用后端渲染html出前端,然后使用js实现交互,这样只需要单独对页面进行制作即可,感觉上不会太麻烦。

但是貌似还是前后端分离了(

前端可以直接用bootstrap的模板,这样就不用自己设置样式了。

1
2
3
技术栈
后端:flask+jinja2
前端:html+css+js+bootstrap

2、连接数据库

后端的话,由于要使用非关系型数据库和关系型数据库,所以需要将数据分开存储,但是两个数据库之间又会有交互。

本来是打算用信息队列的,但是感觉如果数据量不是很大的话使用flask直接调用api进行交互也可以,所以先尝试一下flask

将关系性比较强的用户,题目信息等数据存储到关系型数据库中
将关系性比较弱的题目内容等数据存储到非关系型数据库中

1
2
3
技术栈:
非关系型数据库:redis
关系型数据库:opengauss/psql(雾,是mysql

3、明确功能

题库系统的身份有学生、教师和管理员三种。

根据初步规划,首先需要一个index页面,index页面需要一个跳转到register和login的页面,但是register只能注册学生,教师需要通过管理员才能注册,但是所有用户都是共用一个login页面(很显然是因为我懒得多写一个admin_login页面)。

以上是login部分,login成功之后显然需要一个logout页面,logout之后会直接跳转到index,然后清除登录痕迹,应该是用flask_session实现,还没想好,可能还需要cookie来实现登录持久化。

如果登录失败的话仍然跳转到register,但是清空表单的内容

学生用户登录成功的话可以跳转到student页面,会显示考试的科目,分为考试完成或者未完成的科目,考试完成的不能再次考试并且会显示成绩。点击完成的考试会弹出提示显示已完成,点击未完成的考试则弹出是否开始考试的弹窗,点击开始考试之后跳转到exam页面,exam页面会显示考题,由于懒得对学生是否进行了考试作出检验,此处如果直接点击提交试卷就会结束考试,跳转回student页面,然后显示成绩(我真是好人吧,直接显示成绩)。

教师用户登录成功之后跳转到teacher页面,在该页面也会显示课程,但是只会显示与该教师绑定的课程,一个教师可以绑定多个教师,通过管理员可以进行设置。在教师页面可以通过点击课程标签进入question的dashboard,通过dashboard的表单可以对该科目的题库进行增删改查。

管理员用户登录成功之后可以跳转到admin页面,在该页面会显示用户和题库两个标签,点击进入user或者question的dashboard,可以对整个数据库进行修改。

总结一下,需要开发的路由有index、register、login、logout、student、teacher、admin、user、question,一共是9个。

4、前后端开发

由于写css是一件非常痛苦的事情,所以这里使用bootstrap来解放双手。

1
从https://getbootstrap.com下载编译好的css和js文件,然后解压,将js和css目录存放到project目录下的static目录中

image-20241226210510255

目录结构如上,通过flask对路由进行管理,然后使用jinja2对templates进行渲染。

admin_routes显示admin页面

auth_routes用于注册、登录和登出

exam_routes显示考试页面

index_routes用于处理首页、无效路由和角色跳转

question_routes用于处理question相关的数据

student_routes显示student主页

subject_routes用于处理subject相关的数据

teacher_routes显示teacher主页

5、数据库结构

examsystem需要的数据表

表users,包含id、username、password、email、role、created_time、updated_time、ip

表question,包含id、title、description、type、difficulty、subject_id、creator_id、created_time、updated_time

表subject,包含id、name、created_time、updated_time、exam_duration

表usersubject,包含user_id、subject_id,exam_done(用于表示科目和用户之间的关系,也可以表示考试是否完成)

表answers,包含id、questiond_id、answer_text、created_time、updated_time

表choices,包含id、question_id、content、is_correct、created_time、updated_time(用于选择题)

表exam_records,包含id、user_id、subject_id、score、start_time、end_time、created_time

表exam_questions,包含id、exam_record_id、question_id、user_answer、is_correct、created_time

表logs,包含id、user_id、action、log_type、details、created_time

1
2
3
4
5
6
7
8
9
10
用户表存储系统用户的信息,如管理员、教师、学生等。
题目表存储题目的基础信息。
科目表管理题目的分类。
用户科目表管理用户和科目的联系,表示考试是否完成
答案表存储题目的正确答案或标准答案。
选项表用于选择题的选项信息。
考试记录表记录用户的考试信息。
考试题目表记录每次考试的题目与用户的作答信息。
日志表记录系统操作日志,包括用户行为和系统异常等。
还需要添加索引、约束和级联删除,这里不作赘述,可以看mysql开发部分的模型源码

由于需要同时使用两种不同类型的数据库,所以使用关系型数据库存储数据,然后使用非数据型数据库存储缓存即可。

6、mysql开发

使用SQLAlchemy定义模型

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
from . import db
from datetime import datetime, timedelta

class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(255), unique=True, nullable=False)
password = db.Column(db.String(255), nullable=False)
email = db.Column(db.String(255), unique=True)
role = db.Column(db.String(50), nullable=False)
ip = db.Column(db.String(45), nullable=True)
created_time = db.Column(db.DateTime, default=datetime.utcnow() + timedelta(hours=8))
updated_time = db.Column(db.DateTime, default=datetime.utcnow() + timedelta(hours=8), onupdate=datetime.utcnow() + timedelta(hours=8))

# 添加关系属性,用于删除用户时级联删除用户关联的科目
user_usersubjects = db.relationship('UserSubject', backref='user', cascade='all, delete-orphan')
user_examrecords = db.relationship('ExamRecord', backref='user', cascade='all, delete-orphan')
user_questions = db.relationship('Question', backref='user', cascade='all, delete-orphan')

class Question(db.Model):
__tablename__ = "questions"
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(255), nullable=False, unique=True)
description = db.Column(db.Text, nullable=False)
type = db.Column(db.String(50), nullable=False)
difficulty = db.Column(db.String(50), nullable=False)
subject_id = db.Column(db.Integer, db.ForeignKey("subjects.id"), nullable=False)
creator_id = db.Column(db.Integer, db.ForeignKey("users.id"),nullable=False)
created_time = db.Column(db.DateTime, default=datetime.utcnow() + timedelta(hours=8))
updated_time = db.Column(db.DateTime, default=datetime.utcnow() + timedelta(hours=8), onupdate=datetime.utcnow() + timedelta(hours=8))

# 添加关系属性,用于删除问题时级联删除问题关联的选项和答案
questions_choices = db.relationship('Choice', backref='question', cascade='all, delete-orphan')
questions_answers = db.relationship('Answer', backref='question', cascade='all, delete-orphan')
exam_questions = db.relationship('ExamQuestion', backref='question', cascade='all, delete-orphan')

class Subject(db.Model):
__tablename__ = "subjects"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=False, unique=True)
exam_duration = db.Column(db.Integer, nullable=False)
created_time = db.Column(db.DateTime, default=datetime.utcnow() + timedelta(hours=8))
updated_time = db.Column(db.DateTime, default=datetime.utcnow() + timedelta(hours=8), onupdate=datetime.utcnow() + timedelta(hours=8))
# 添加关系属性,用于删除科目时级联删除科目关联的问题
subject_questions = db.relationship('Question', backref='subject', cascade='all, delete-orphan')
subject_usersubjects = db.relationship('UserSubject', backref='subject', cascade='all, delete-orphan')
subject_examrecords = db.relationship('ExamRecord', backref='subject', cascade='all, delete-orphan')

class UserSubject(db.Model):
__tablename__ = "user_subjects"
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), primary_key=True)
subject_id = db.Column(db.Integer, db.ForeignKey('subjects.id'), primary_key=True)
exam_done = db.Column(db.Boolean, nullable=False)

class Answer(db.Model):
__tablename__ = "answers"
id = db.Column(db.Integer, primary_key=True)
question_id = db.Column(db.Integer, db.ForeignKey("questions.id"), nullable=False)
answer_text = db.Column(db.Text, nullable=False)
created_time = db.Column(db.DateTime, default=datetime.utcnow() + timedelta(hours=8))
updated_time = db.Column(db.DateTime, default=datetime.utcnow() + timedelta(hours=8), onupdate=datetime.utcnow() + timedelta(hours=8))

class Choice(db.Model):
__tablename__ = "choices"
id = db.Column(db.Integer, primary_key=True)
question_id = db.Column(db.Integer, db.ForeignKey("questions.id"), nullable=False)
content = db.Column(db.String(255), nullable=False)
is_correct = db.Column(db.Boolean, nullable=False)
created_time = db.Column(db.DateTime, default=datetime.utcnow() + timedelta(hours=8))
updated_time = db.Column(db.DateTime, default=datetime.utcnow() + timedelta(hours=8), onupdate=datetime.utcnow() + timedelta(hours=8))

class ExamRecord(db.Model):
__tablename__ = "exam_records"
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)
subject_id = db.Column(db.Integer, db.ForeignKey("subjects.id"), nullable=False)
score = db.Column(db.Float, nullable=False)
start_time = db.Column(db.DateTime, nullable=False)
end_time = db.Column(db.DateTime, nullable=False)
created_time = db.Column(db.DateTime, default=datetime.utcnow() + timedelta(hours=8))

# 添加关系属性,用于删除考试记录时级联删除考试记录关联的考试问题
exam_questions = db.relationship('ExamQuestion', backref='exam_record', cascade='all, delete-orphan')

class ExamQuestion(db.Model):
__tablename__ = "exam_questions"
id = db.Column(db.Integer, primary_key=True)
exam_record_id = db.Column(db.Integer, db.ForeignKey("exam_records.id"), nullable=False)
question_id = db.Column(db.Integer, db.ForeignKey("questions.id"), nullable=False)
user_answer = db.Column(db.Text, nullable=False)
is_correct = db.Column(db.Boolean, nullable=False)
created_time = db.Column(db.DateTime, default=datetime.utcnow() + timedelta(hours=8))

class Log(db.Model):
__tablename__ = "logs"
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, nullable=False)
action = db.Column(db.String(255), nullable=False)
log_type = db.Column(db.String(50), nullable=False)
details = db.Column(db.Text, nullable=True)
created_time = db.Column(db.DateTime, default=datetime.utcnow() + timedelta(hours=8))

通过jinja2和routes将数据渲染到html中

7、redis开发

使用redis缓存提高查询的速度,主要针对question_routes和user_routes进行缓存,在exam_routes,teacher_routes和admin_routes使用缓存加快查询,因为这几个路由的数据量比较大。

对user_routes进行修改

1
2
3
4
5
6
read:查询redis中是否有user数据
由于有很多个user数据,并且需要全部取出,在存储时可以存储在list中
list具有顺序,相对来说比较适合
add:向redis中插入数据user(id,username,email)
edit:不需要修改,因为id,username,email都是修改不了的
delete:从列表中删除特定user数据

对question_routes进行修改

1
2
3
4
read:查询redis中是否有questions
add:向redis中插入数据questions和options
edit:修改questions和options
delete:删除questions和options

对exam_routes进行修改

1
在加载题目时优先从redis中加载

样例

1
2
3
4
5
6
choices:
{"id": 17, "question_id": 16, "content": "11111111", "is_correct": true}
questions:
{"id": 12, "title": "qwe", "description": "qwer", "type": "true_false", "difficulty": "easy", "subject": "\u8ba1\u7b97\u673a", "creator": 4}
users:
{"id": 9, "username": "123", "email": "123@qq.com", "role": "student"}

8、前后端与数据库结合

前端

前端接收从后端传入的数据,并使用form+ajax的形式将数据传输到后端

后端

admin_routes:通过flask_session检验用户是否为admin身份,然后通过查询数据库中user的基本信息,将数据传入到admin.html

auth_routes:用于验证用户状态。

1、register:分为get和post两种状态。get返回注册页面;post读取表单传入的user信息,校验两次输入的密码是否一致,检查用户或者邮箱是否已经存在。检验通过,将新用户写入mysql的user表,然后写入redis的users列表中,再将注册事件写入log表,跳转到注册成功页面。(直接注册的用户没有绑定科目,需要使用admin权限进行绑定)

2、login:分为get和post两种状态。get返回登录页面;post读取表单传入的user信息,从数据库中查询该用户,检查用户是否存在,密码是否正确。密码正确,生成flask_session,更新数据库中用户的数据,(ip和登录时间,这里不需要更新redis,因为login只会更新ip和登陆时间,这些没有写入redis中),log写入login事件,跳转到dashboard,再通过role进行判断跳转到不同的主页

3、logout:只接受post请求,如果用户还没有登录则返回到登录页面。如果已经登录,且为post请求,并且确认要退出的话,清除session,log写入logout事件

exam_routes:只接受post请求。检查用户是否登录,已登录检查用户是否为student。post请求分为两种action。

1、start:表示考试开始。首先通过表单中的科目查询考试的持续时间,再查询考试是否已经完成。如果未完成,先检查redis中是否存在题目数据,如果不存在再从mysql中获取题目数据。将答题记录写入exam_records,先检查是否是第一次进入考试(如果没有提交就算考试没完成),因为exam_recodes只能存在一条,所以如果已经存在则改为更新数据。log写入start事件。
(由于此处的持续时间是在前端实现的自动提交,所以其实是个聊胜于无的东西,但是这里由于时间原因就没有修改了)

2、submit:表示提交答案。同样的检查考试是否完成。如果未完成,获取提交的答案,通过question_id从answer中获得正确答案,将提交的答案转化为正确的答案格式,跟正确答案进行比对,计算出分数。将答题记录写入exam_question表,将分数和考试结束时间写入exam_records表,将user_subject表中的exam_done修改为true。log写入submit事件

index_routes:用于处理各种繁杂功能的路由。

1、index:如果用户未登录,返回到主页;如果用户已经登录,转到用户主页。

2、dashboard:如果用户未登录,返回到登录页;如果用户已经登录,准到yoghurt主页。

3、<path:path>:通配符,将未定义的页面都返回到主页

question_routes:用于处理question表。只接收post请求,先检查用户是否登录再检查用户是否为学生。如果用户为老师,只能修改绑定科目的题目,如果为admin,则无限制。分为四种action。

1、read:如果是老师,查询对应的科目,再查询题目,先对redis进行查询,再从mysql中查询。如果是管理员,直接查询题目,先对redis进行查询,再从mysql中查询。log写入read事件。

2、add:先检查问题的题目是否重复。如果用户为老师,检查科目是否为老师的绑定科目,再检查科目是否存在。检验通过,获取表单数据,根据题目类型进行分类。如果题目为单选题,检查答案是否为一个,检查答案是否在abcd的范围内,检验通过,写入question数据到mysql,写入question数据到redis,log写入add事件,写入choice数据到mysql,写入choice数据到redis,log写入add事件,写入answer数据到mysql,log写入add事件;如果题目为多选题,检查答案是否多于一个,检查答案是否在abcd的范围内,对答案进行去重,排序和字符串转换,转为正确的答案格式,写入数据和日志;如果题目为判断题,判断答案是否为true或者false,写入数据和日志;如果题目为填空题,写入数据和日志。

3、edit:检查题目是否存在。如果用户为teacher,检查teacher是否绑定科目,是否为创建者。检查科目是否存在以及是否匹配,检查题目类型是否匹配。接收表单数据,能修改的只有题干和题目难度和答案,接收数据更新mysql和redis。写入日志。

4、delete:检查题目是否存在。检查teacher用户是否为创建者。删除mysql和redis中数据,写入日志。

student_routes:检查是否登录,检查是否是学生,获取学生信息

subject_routes:只接受post请求,先检查是否登录,再检查是否为admin,存在四种action。(由于科目一般不是特别多就没上redis)

1、read:读取所有科目,写入日志。

2、add:表单获取数据,检查科目是否已经存在,添加科目到mysql,写入日志。

3、edit:表单获取数据,检查科目是否存在,存在的话进行修改,可以对持续时间进行修改。(当然我前面也说了这个功能基本没用)写入数据和日志到mysql

4、delete:表单获取数据,检查科目是否存在,存在的话删除,写入数据和日志到mysql。

teacher.routes:检查用户是否登录,检查是否为老师,查询数据插入template

user_routes:检查用户是否登录且为admin,只接受post请求,有四种action。

1、read:先从redis查询再从mysql查询,写入日志。

2、add:从表单获取数据,检查用户是否已经存在,不存在则添加数据到mysql和redis,再绑定科目,写入数据到user_subject,绑定科目需要去重。写入进行add操作的日志和add的日志。(写入redis的是id,email,role)

3、edit:获取数据,检查用户是否存在,绑定新的科目。写入进行edit操作的日志和edit的日志。

4、delete:获取数据,检查用户是否存在且用户名是否为admin,删除mysql和redis中的数据,写入日志。

9、部署流程

由于在windows忘记用虚拟环境了,所以requirements.txt里面的库可能不是很全

1
2
3
4
5
6
7
8
python3 run.py
(报错的话缺哪个库就安装哪个,如果没报错就停止然后直接下面的数据库初始化,当然mysql里面要先建好数据库)
flask db init
flask db migrate -m "Initial"
flask db upgrade
再执行
python3 run.py
浏览器访问127.0.0.1:5000,你要是有兴趣开内网也可以自己开(

课设链接

1
https://github.com/zx2023qj/database_keshe