面试刷题平台
参考github开源项目:liyupi/mianshiya-next
需求分析
管理员可以创建题库、题目和题解,并批量关联题目到题库;用户可以注册登录、分词检索题目、在线刷题并查看刷题记录日历等。
项目涉及大量企业级新技术的讲解,比如使用数据库连接池、热 Key 探测、缓存、高级数据结构来提升性能。通过流量控制、熔断、动态 IP 黑白名单过滤、同端登录冲突检测、分级反爬虫策略来提升系统和内容的安全性。
技术选型
基于 Next.js 服务端渲染 + Spring Boot + Redis + MySQL + Elasticsearch 的 面试刷题平台。
主流框架 & 特性
Spring Boot 2.7.x(贼新)
Spring MVC
MyBatis + MyBatis Plus 数据访问(开启分页)
Spring Boot 调试工具和项目处理器
Spring AOP 切面编程
Spring Scheduler 定时任务
Spring 事务注解
数据存储
MySQL 数据库
Redis 内存数据库
Elasticsearch 搜索引擎
腾讯云 COS 对象存储
工具类
Easy Excel 表格处理
Hutool 工具库
Apache Commons Lang3 工具类
Lombok 注解
使用drawio绘制流程图
数据库表设计
根据流程分析,需要用户表存储管理员和普通用户,题库表存题库,题目表存题目,因为题目题库是多对多关系,所以需要中间表题库—题目关联表存储映射关系。这里使用帖子模版改造,是比较通用的单体项目数据库设计。
常见字段:
createTime和updateTime:一般由工具自动插入数据。
isDelete :逻辑删除。
# 数据库初始化
-- 创建库
create database if not exists mianshiti;
-- 切换库
use mianshiti;
-- 用户表
create table if not exists user
(
id bigint auto_increment comment 'id' primary key,
userAccount varchar(256) not null comment '账号',
userPassword varchar(512) not null comment '密码',
unionId varchar(256) null comment '微信开放平台id',
mpOpenId varchar(256) null comment '公众号openId',
userName varchar(256) null comment '用户昵称',
userAvatar varchar(1024) null comment '用户头像',
userProfile varchar(512) null comment '用户简介',
userRole varchar(256) default 'user' not null comment '用户角色:user/admin/ban',
editTime datetime default CURRENT_TIMESTAMP not null comment '编辑时间',
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间',
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
isDelete tinyint default 0 not null comment '是否删除',
index idx_unionId (unionId)
) comment '用户' collate = utf8mb4_unicode_ci;
-- 题库表
create table if not exists question_bank
(
id bigint auto_increment comment 'id' primary key,
title varchar(256) null comment '标题',
description text null comment '描述',
picture varchar(2048) null comment '图片',
userId bigint not null comment '创建用户 id',
editTime datetime default CURRENT_TIMESTAMP not null comment '编辑时间',
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间',
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
isDelete tinyint default 0 not null comment '是否删除',
index idx_title (title)
) comment '题库' collate = utf8mb4_unicode_ci;
-- 题目表
create table if not exists question
(
id bigint auto_increment comment 'id' primary key,
title varchar(256) null comment '标题',
content text null comment '内容',
tags varchar(1024) null comment '标签列表(json 数组)',
answer text null comment '推荐答案',
userId bigint not null comment '创建用户 id',
editTime datetime default CURRENT_TIMESTAMP not null comment '编辑时间',
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间',
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
isDelete tinyint default 0 not null comment '是否删除',
index idx_title (title),
index idx_userId (userId)
) comment '题目' collate = utf8mb4_unicode_ci;
-- 题库题目表(硬删除)
create table if not exists question_bank_question
(
id bigint auto_increment comment 'id' primary key,
questionBankId bigint not null comment '题库 id',
questionId bigint not null comment '题目 id',
userId bigint not null comment '创建用户 id',
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间',
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
UNIQUE (questionBankId, questionId)
) comment '题库题目' collate = utf8mb4_unicode_ci;
-- 功能扩展:用户个人主页资料修改
ALTER TABLE user
ADD phoneNumber VARCHAR(20) COMMENT '手机号',
ADD email VARCHAR(256) COMMENT '邮箱',
ADD grade VARCHAR(50) COMMENT '年级',
ADD workExperience VARCHAR(512) COMMENT '工作经验',
ADD expertiseDirection VARCHAR(512) COMMENT '擅长方向';
-- 功能扩展:AI 模拟面试功能
-- 模拟面试表
create table if not exists mock_interview
(
id bigint auto_increment comment 'id' primary key,
workExperience varchar(256) not null comment '工作年限',
jobPosition varchar(256) not null comment '工作岗位',
difficulty varchar(50) not null comment '面试难度',
messages mediumtext null comment '消息列表(JSON 对象数组字段,同时包括了总结)',
status int default 0 not null comment '状态(0-待开始、1-进行中、2-已结束)',
userId bigint not null comment '创建人(用户 id)',
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间',
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
isDelete tinyint default 0 not null comment '是否删除(逻辑删除)',
index idx_userId (userId)
) comment '模拟面试' collate = utf8mb4_unicode_ci;
增删改查
使用MybatisX插件生成数据库表的增删改查代码
这里使用了Mybatis-Plus,lombok,取消勾选生成重写Object三个的方法。
放在另外的包内,按需加入项目,包括实体类,mapper,service和xml文件。service包不引入,使用自定义代码生成器。
使用自定义代码生成器生成代码
controller包下引入生成的代码
实体类包新增三个DTO
service下新增生成的代码
修改DTO/VO字段
根据业务需求,将前后端请求可能用到的字段从数据库实体类添加到DTO/VO
从用户权限来看,可以适当扩展字段,比如更新的dto字段比新增更多,只有管理员权限可以使用
权限高低:数据库管理员>超级管理员>管理员>普通用户
然而部分系统的设计,数据库是以二进制存储数据的,这时候开发者权限最大。
将新增的请求类都检查一遍,业务要求该添加的字段添加上。
代码生成器没有连接数据库动态获取生成的字段,生成的类字段就很可能牛头不对马嘴,不论使用多么优秀完美的生成器,业务的需求肯定是动态调整的,字段就是要根据业务调整。+
+
甚至有的生成的类根本就不需要,直接删除即可,比如不需要编辑QuestionBankQuestionEditRequest,一次性生成的数据库表关联,没有编辑的必要。
VO和实体的一些区别,比如数据库存的是json数组,返回给前端的是列表。
也比如,数据库实体可能没有的字段,前端VO返回需要连表查询后封装新的字段返回。像是创建的用户信息。
Controller层代码
同一个功能两种权限,限制用户一次获取所有分页,防止爬虫,管理员可以拿到所有数据。
这里就是根据前段传入的实体类不同进行分类,给用户返回的是VO封装类,给管理员返回的实体类。
如果这种场景很多,就可能需要分包。