我在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 是隐形的未来。 自动记录的 createdAtupdatedAt,现在看起来没用,但之后做练习记录、复盘回顾、学习进度统计时,全都要靠它。数据库设计时的"多想一步",就是为未来省事。


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

题库列表页按分类筛选,接口要能接收不同的筛选参数。我用的是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,欢迎在评论区交流你的全栈学习经验。

相关推荐
雨辰AI1 小时前
信创数仓分层建模|国产数据库 ODS/DWD/DWS 分层落地规范(金仓 / 达梦 / 高斯适配)
数据库·云原生·政务
风哥2号1 小时前
MySQL8.x/9.x数据库安装自动化全过程-Fgedu
数据库·mysql自动化安装
疯狂打码的少年2 小时前
【数据库技术】SQL数据查询(SELECT基本语法)
数据库·笔记·sql·oracle
程序员黎剑2 小时前
Redis缓存击穿:热点Key过期打崩数据库的3种解决方案
数据库·redis·缓存
Carl_.Net软开2 小时前
NSSM后台启动influx时序数据库
数据库·时序数据库
IPdodo_2 小时前
静态 IP 访问异常排查:403/429 归因与迁移验收
服务器·网络·数据库·python·网络协议·php·性能测试
DevOpenClub2 小时前
PDF 多格式解析如何避免混用输出:TEXT、HTML、XML 与 TAG 数据契约
xml·前端·数据库·pdf·html
meilindehuzi_a3 小时前
从域名到数据库:React + Node.js 项目部署全流程与用户访问链路
数据库·react.js·node.js
泡干脆面就番茄3 小时前
MySQL_流程控制_分组查询与连接查询详解
数据库·mysql