我在React全栈项目中搭建了一个完整题库

我是小ao,大三计算机在读。最近,我在做一个AI驱动的面试准备平台------面试克星。用户系统做完后,摆在面前的问题是:题从哪来,题怎么展示,题怎么管理?

这篇文章记录我从"接入DeepSeek API"到"题库完整闭环"的实践过程。没有高深理论,全是踩坑和思考。


一、题从哪来?用Prompt批量生成种子数据

我一开始想过爬虫。但冷静下来算了一笔账:爬虫要学反爬、学解析、还要处理版权问题。而我的后端已经接好了DeepSeek API,为什么不直接让它出题?

我设计了一个出题Prompt,要求AI按分类每个生成10道,难度按比例分配。核心是输出格式锁死为JSON数组,方便后续直接入库。

结果出乎意料的好。AI生成了60道题,我抽查了几道,质量完全可以作为基础题库。这次实践还为我后续的AI出题功能奠定基础。


二、怎么入库?写一个seed脚本

拿到了JSON,接下来要把它导入MongoDB。我第一反应是手动一条条插入,但60条数据手动插太蠢了。正确姿势是写一个seed脚本。

脚本逻辑就四步:

ts 复制代码
// 连接数据库
await connectDB()
// 清空旧数据(避免重复)
await Question.deleteMany({})
// 批量插入
await Question.insertMany(data)
// 断开连接
mongoose.disconnect()

看起来简单,但我踩了两个真实的坑。

坑1:connectDB() 没加 await

数据库连接是异步的,不等待就直接插入,等于"快递员还没到,你就开始收货"。后果就是脚本卡住不动,或者报uri参数为undefined。

坑2:忘记加载 .env

脚本文件里不主动加载环境变量,process.env.MONGO_URI 就是 undefined。必须:

ts 复制代码
import dotenv from 'dotenv';
dotenv.config();

这两个坑都不难,但第一次写脚本的人一定会遇到。每个坑都让我更理解"异步"和"环境变量"这两个基础概念。


三、Question模型设计:enum和timestamps

Question模型本身不复杂,但有两点值得说。

第一,enum 约束让数据更干净。 题目的类型、分类、难度都是有限的选项,用 enum 锁死,防止脏数据混进来。这也逼着我去对齐"AI生成的JSON字段"和"模型字段"------两边对不上,insertMany 直接报验证错误。

第二,timestamps: true 是隐形的未来。 自动记录的 createdAt 和 updatedAt,现在看起来没用,但之后做练习记录、复盘回顾、学习进度统计时,全都要靠它。数据库设计时的"多想一步",就是为未来省事。


四、题目接口:动态筛选条件的构造

题库列表页按分类筛选,接口要能接收不同的筛选参数。我用的是query参数:

txt 复制代码
GET /api/questions?category=JavaScript
GET /api/questions?difficulty=easy

后端逻辑很简单,但有个细节值得注意:

ts 复制代码
const filter: any = {}
if (category) filter.category = category
if (difficulty) filter.difficulty = difficulty
const questions = await Question.find(filter)

动态构造查询条件------前端传什么,就筛选什么;不传,就返回全部。这个模式在后续做练习抽题、收藏筛选时都能复用。


五、前端列表页:从左到右的布局与数据流

列表页的交互设计是:左侧分类栏,右侧题目列表。 点击分类,右侧题目实时切换。

技术上就是 flex 布局加上"选中状态"管理。但真正让我重新理解的,是useEffect 依赖数组的威力:

ts 复制代码
const [selectedCategory, setSelectedCategory] = useState(categories[0])

useEffect(() => {
  // 请求题目
}, [selectedCategory])

selectedCategory 变化时,useEffect 自动重新执行,请求新分类的题目。这在Vue里是用watch实现的,在React里就是依赖数组。 换的是语法,不变的是"状态驱动视图"的底层逻辑。


六、详情页:useParams从URL里取id

点击题目跳转到详情页,路径是 /questions/:id。React Router 里,从URL里取动态参数靠 useParams:

ts 复制代码
const { id } = useParams()

拿到id后,请求 /api/questions/${id},展示题目、选项、正确答案、解析。后端接口用 Question.findById(req.params.id) 查单条,查不到返回404。


写在最后

这个题库模块做完,我最大的感受是:前端和后端之间,数据流的对齐。

  • AI生成的JSON字段,必须和Question模型对齐
  • 后端返回的 { questions: [...] },前端必须取 res.data.questions 而不是整个 res.data
  • URL里的斜杠要 encodeURIComponent 编码
  • 每个受保护接口都要带上 Authorization 头

这些都不是高深的知识,但每错一次,就是一次真实的成长。项目驱动的学习,就是让你在真实场景里,把这些细节刻进肌肉记忆。

现在题库跑通了。下一步,就是让用户真正"练习"起来------这是从"浏览"到"互动"的关键一步。


我是小ao,欢迎在评论区交流你的全栈学习经验。

相关推荐
Data analyse45611 分钟前
Session超时与浏览器休眠的计时校验
大数据·前端·数据库·数据分析
NineData12 分钟前
Oracle 实时同步到 Doris 怎么做?全量迁移、CDC 增量复制与数据校验指南
数据库·oracle
码流子33 分钟前
高速场景视频接入:GB/T 28181 级联与 RTSP 取流,一条链路讲清楚
大数据·数据库·人工智能·物联网·音视频
m0_7520356340 分钟前
Redis 数据结构总结
数据结构·数据库·redis
iCxhust1 小时前
51单片机电力载波应用开发(九)51产品化
c语言·数据库·嵌入式硬件·mysql·51单片机·软件工程·c51
tryxr1 小时前
如何分析一个慢sql
数据库·sql·mysql·慢sql
泡海椒1 小时前
JQuickExcel 大文件排障与调优:从实际路径定位 OOM、临时盘和慢任务
开发语言·数据库·缓存·oracle·excel
java_logo2 小时前
Docker 部署 MariaDB:轻松搭建开源关系型数据库
数据库·docker·容器·开源·mariadb·轩辕镜像·docker部署教程
tryxr2 小时前
什么情况下索引会失效
数据库·mysql·索引失效·索引
guo_wen_qiang2 小时前
redis持久化
数据库·redis·分布式