做这个项目之前,我先说说为什么会碰它。现在“计算思维 + 人工智能”这类学习平台在高校课程设计和毕业设计里出现频率非常高,很多同学一开始拿到这个题目,第一反应是先堆一堆AI概念上去,结果系统做得四不像。我当时定下这个“基于SpringBoot的计算思维与人工智能学习网站”的目标很简单:用SpringBoot老老实实把用户、课程、训练、测验、进度这一整条学习链路做通,再在前端展示计算思维和人工智能知识点。这个项目既能覆盖SpringBoot的核心开发流程,又能把内容管理、在线学习、考试评测这些典型场景串起来,对刚开始接触全栈开发的人帮助很大。
这篇文章我会按实际做项目的顺序来拆,从需求定位、数据库设计、后端接口实现、前端联调到部署上线,再把我在开发过程中踩过的一些坑列出来。不用把它当成一个纯粹的“教程”,更像是一次完整项目复盘。你如果是拿这个题目写毕业设计或者课设,可以直接照着骨架去改;如果你是单纯想练SpringBoot,这套业务也比“增删改查员工表”有说服力得多。
1. 项目整体设计与思路拆解
1.1 核心需求定位
不要一上来就写代码,先搞清楚这个网站到底是给谁用、要解决什么。这个题目看起来很宽泛,但落到具体场景无非就是两类角色:
- 学生用户:注册登录、浏览课程、查看计算思维和AI知识点、在线刷题、参加测验、查看学习进度。
- 管理员:管理课程分类、管理章节内容、维护题库、查看用户学习数据、处理公告。
核心模块基本围绕“内容展示 + 学习行为记录 + 自我评测”展开。计算思维部分通常包括分解、模式识别、抽象、算法设计这四大块内容;人工智能部分则偏科普向,比如机器学习、深度学习、自然语言处理、计算机视觉、AI伦理等。设计的时候一定要把内容分类和课程章节结构理清楚,不然后面管理后台会特别痛苦。
我当时把系统拆成四个核心模块:用户模块、课程模块、训练测验模块、数据统计模块。后台采用RBAC权限模型,管理员和普通用户分开,接口层面用拦截器校验Token和角色,没有把权限业务全部压在Shiro或者Security上,因为项目规模不大,自己控制会更直观。
1.2 技术选型:为什么锁定SpringBoot
技术选型上我几乎没有纠结,后端就是用SpringBoot。原因有三点:
第一,SpringBoot对初学者极其友好。项目用Maven管理依赖,SpringBoot的starter机制让你基本不需要手动维护依赖版本,常规的Web开发只需要引 spring-boot-starter-web,连接数据库加一个 spring-boot-starter-jdbc 或者 mybatis-plus-boot-starter,几分钟内就能把工程跑起来。
第二,SpringBoot天然适合做前后端分离。用 @RestController 返回JSON数据,配合Vue或者前端模板都能很快对接。我这个项目最终选了前后端分离的方案,后端只提供接口,前端用Vue + Element Plus,开发效率高,部署也灵活。
第三,生态成熟。像权限校验、文件上传、定时任务这些功能,SpringBoot都有比较标准的做法,出了问题也好搜资料。
版本这里我多说一句,如果你用的是SpringBoot 3.x,注意JDK版本要求是17+,很多学校的教材还在用2.x,这里不要照搬网上旧教程,否则光环境问题就能耗掉你大半天。我的项目最终用的是SpringBoot 2.7.18,主要考虑到MyBatis-Plus和部分第三方工具的兼容性,这个版本比较稳。
1.3 系统模块与架构划分
整个系统的架构其实很简单,就是经典的前后端分离三层架构:
code复制前端 Vue项目(用户端 + 管理端)
↓ HTTP/JSON + JWT
后端 SpringBoot服务
├─ Controller层:接收请求、参数校验、返回统一Result结构
├─ Service层:业务逻辑、事务控制
└─ Mapper层:MyBatis-Plus操作数据库
↓
MySQL 8.0数据库
模块划分上,我用了包名分层的方式:
com.edu.controller:控制层com.edu.service:业务层com.edu.mapper:数据访问层com.edu.entity:实体类com.edu.config:配置类(跨域、拦截器、异常处理)com.edu.common:统一返回体、枚举、工具类
这种分层的好处是职责清晰。比如你后面要加一个“错题本”功能,代码路径和业务边界都非常明确,新增一个模块不会影响到已有功能。我见过不少同学把业务代码全写在Controller里,一时爽,后面改需求直接爆炸,别学。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心功能实现与代码落地
2.1 用户登录与JWT权限设计
用户模块是整个项目的地基。这里我不建议用Session,默认就用JWT实现无状态登录,因为前后端分离场景下JWT更好用,也不用考虑Session跨域共享的问题。
整体流程是:用户提交用户名和密码 → 后端校验 → 生成JWT返回给前端 → 前端把Token存在localStorage → 之后每次请求通过请求头 Authorization: Bearer <token> 带上 → 后端拦截器校验Token并把用户信息放进上下文。
核心代码分三块。第一块是登录接口:
java复制@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private UserService userService;
@PostMapping("/login")
public Result login(@RequestBody LoginDTO dto) {
// 校验用户名密码
User user = userService.login(dto.getUsername(), dto.getPassword());
// 生成JWT,有效期2小时
String token = JwtUtil.generateToken(user.getId(), user.getRole());
return Result.success(new LoginVO(user, token));
}
}
注意业务逻辑里密码不能明文存储。我在 UserService 里用BCrypt加密,存储时直接存加密串,登录时再用 BCryptPasswordEncoder.matches() 校验。
第二块是拦截器。创建一个 JwtInterceptor 实现 HandlerInterceptor,在 preHandle 中解析Token,并从Redis中判断当前Token是否有效(这里可以简单用数据库查询替代,但Redis性能好很多):
java复制@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("Authorization");
if (StringUtils.isBlank(token) || !token.startsWith("Bearer ")) {
throw new BizException(401, "未登录");
}
// 解析token并校验角色
Claims claims = JwtUtil.parseToken(token.replace("Bearer ", ""));
UserContext.set(claims);
return true;
}
第三块是管理员权限校验。同一套拦截器里可以通过自定义注解来区分权限要求,也可以在业务代码里判断角色,我是用的自定义注解 @RequireRole("admin"),在拦截器里读取注解元数据做校验,实现起来也比较直接。
2.2 课程与章节管理
课程模块是这个学习网站的核心内容模块。我设计了课程表、章节表、视频表、资源表四张表,基本信息如下:
sql复制CREATE TABLE course (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(100) NOT NULL COMMENT '课程标题',
category_id BIGINT NOT NULL COMMENT '分类ID,1=计算思维,2=人工智能',
summary VARCHAR(500) COMMENT '课程简介',
cover_url VARCHAR(255) COMMENT '封面图',
difficulty TINYINT DEFAULT 1 COMMENT '难度等级',
status TINYINT DEFAULT 1 COMMENT '0下架 1上架',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
管理员在后台可以对课程进行增删改查、上下架操作。用户端则展示上架状态的课程,点进课程后可以看到章节列表。这里关键点是章节表和课程表构成一对多关系,查询课程详情时要联表查出章节列表,用MyBatis-Plus的 QueryWrapper 加一个循环查N+1问题的优化,我直接在主查询后批量查章节,避免在循环里执行SQL。
在内容实现上,计算思维章节我通常按四类来组织:
- 分解:把复杂问题拆成小问题
- 模式识别:从多个问题中找出共性
- 抽象:提取核心特征,忽略无关细节
- 算法设计:逐步制定解决问题的方案
AI部分则分成AI导论、机器学习基础、深度学习与神经网络、AI应用、AI伦理几个模块。每个章节除了文字内容,还配有示例、视频链接和课后测验,这样课程内容就不是干巴巴的一堆文本,而是有学习闭环的。
2.3 在线测验与自动判分
如果说课程模块是内容展示,那测验模块就是学习效果的验证。我的测验模块支持两种题型:单选题和判断题。计算思维部分我偏向用情境化题目,比如“餐厅每天要统计顾客点餐数据,请选择最适合的抽象方式”这种,AI部分则更偏概念理解和简单应用判断。
题目表结构大致如下:
sql复制CREATE TABLE question (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
course_id BIGINT NOT NULL,
chapter_id BIGINT,
type TINYINT NOT NULL COMMENT '1单选 2判断',
content TEXT NOT NULL COMMENT '题干',
options VARCHAR(1000) COMMENT '选项JSON',
answer VARCHAR(50) COMMENT '正确答案',
analysis TEXT COMMENT '答案解析',
score INT DEFAULT 5
);
测验流程我做成这样:用户进入测验接口,后端按课程和章节随机抽取N道题返回(不返回答案),用户提交答案后后端逐题比对。这里有个容易踩坑的点,就是接口返回题目给前端时,一定不要顺带把 answer 字段也返回,否则前端控制台一看接口数据答案全出来了,测验就没有意义了。我实际开发时专门写了一个 QuestionVO,只返回不带答案的字段。
判分核心逻辑比较简单:
java复制public ExamResultVO submitExam(Long userId, Long examId, List<UserAnswerDTO> answers) {
// 1. 获取所有题目及答案
List<Question> questions = questionMapper.selectList(
new QueryWrapper<Question>().eq("exam_id", examId));
Map<Long, String> answerMap = questions.stream()
.collect(Collectors.toMap(Question::getId, Question::getAnswer));
// 2. 逐题判分
int totalScore = 0;
int correctCount = 0;
for (UserAnswerDTO ans : answers) {
String correctAnswer = answerMap.get(ans.getQuestionId());
if (correctAnswer != null && correctAnswer.equals(ans.getAnswer())) {
totalScore += ans.getScore();
correctCount++;
}
}
// 3. 保存成绩记录
ExamRecord record = new ExamRecord();
record.setUserId(userId);
record.setExamId(examId);
record.setScore(totalScore);
record.setCreateTime(LocalDateTime.now());
examRecordMapper.insert(record);
return new ExamResultVO(totalScore, correctCount, answers.size());
}
自动判分从技术上不复杂,难点在题目本身的认真编辑。如果你只搞了几道题放上去,功能看起来通了,但一让别人体验就会发现内容不够用。这块要多投入精力,至少每个章节配10-20道小题。
2.4 学习进度与数据统计
学习进度跟踪是学习网站区别于普通资源站的关键能力。我这里做了两层:
第一层是章节学习记录。用户点击课程章节时,前端调用一个“记录学习进度”的接口,后端在 learn_record 表里写入或更新记录,表结构包含用户ID、课程ID、章节ID、学习时长、最后学习时间。用户在课程列表页就能看到已经学习到第几章、进度百分比是多少。
第二层是做学习率的统计。用户在个人中心可以看到自己的学习概览:完成了多少课程、总测验次数、平均得分。管理员后台可以看到整体平台的数据:用户数、课程数、活跃用户数、每门课程的学习人数。这些统计SQL不复杂,但要注意用GROUP BY的时候把索引加上,否则数据量大了会慢。
学习记录表我加了联合唯一索引 uk_user_course,防止一条记录被插入多条:
sql复制CREATE TABLE learn_record (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
course_id BIGINT NOT NULL,
chapter_id BIGINT NOT NULL,
learn_duration INT DEFAULT 0 COMMENT '学习时长/秒',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_user_course (user_id, course_id, chapter_id)
);
2.5 后台管理接口
管理端和后端接口是同一个SpringBoot应用,但功能不同。管理端主要提供:
- 仪表盘统计数据接口
- 课程分类管理
- 课程/章节内容维护
- 题目管理(增删改查、批量导入)
- 用户管理(启用/禁用)
- 公告管理
在实现后台时我特别注意了两个点。一是所有写操作都要做参数合法性校验,比如课程标题不能为空、题目选项数量必须是4个、题目的答案必须在选项范围内,这些校验可以用JSR 303注解写DTO,也可以手写,但必须做。二是接口返回结构统一,我封装了一个 Result<T> 类,所有接口返回值统一为 { code, message, data },前端做交互时非常省事,不用每个接口单独处理异常。
3. 数据库设计与关键配置
3.1 完整表结构规划
把整个项目的表列出来是这样的:
| 表名 | 用途 | 关键字段 |
|---|---|---|
| user | 用户表 | username, password, role, email |
| course | 课程表 | title, category_id, summary, status |
| chapter | 章节表 | course_id, title, content, sort |
| question | 题目表 | course_id, chapter_id, type, content, options, answer |
| exam | 测验表 | title, course_id, total_score, duration |
| exam_record | 答题记录表 | user_id, exam_id, score |
| learn_record | 学习记录表 | user_id, course_id, chapter_id, duration |
| notice | 公告表 | title, content, publish_time |
| category | 课程分类表 | name, sort |
这几张表基本覆盖了系统的核心业务。一个常见的问题是:题目表为什么不直接存在章节表里?我的设计是 question 表存 course_id 和 chapter_id 两个外键,这样既能按课程组卷,也能按章节做练习,灵活性更高。
3.2 application.yml 关键配置
SpringBoot配置文件是整个项目的入口配置,我这边核心配置如下:
yaml复制server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/edu_site?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: 123456
redis:
host: localhost
port: 6379
database: 0
mybatis-plus:
mapper-locations: classpath:/mapper/*.xml
global-config:
db-config:
id-type: auto
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
几个关键点:
map-underscore-to-camel-case: true 这个很重要。数据库字段是 create_time,Java实体是 createTime,这个配置打开后MyBatis-Plus会自动做驼峰映射,不用手动写resultMap。
serverTimezone=Asia/Shanghai 是连接MySQL 8.0时经常踩的时区问题。如果你不加这个参数,连接时经常报 The server time zone value '�й���ʱ��' is unrecognized,那是时区设置问题。加了之后基本就正常了。
Redis这里用来做JWT的“一键踢人”功能。管理员把某个用户禁用的时候,删除该用户在Redis中的Token,用户下一次请求就会被拦截下来,不需要等Token自然过期。这个功能体验很好,推荐做上。
3.3 MyBatis-Plus集成与MVP式写法
MyBatis-Plus对这类管理系统的开发效率提升是非常明显的。引入依赖:
xml复制<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3</version>
</dependency>
然后在启动类上加 @MapperScan:
java复制@SpringBootApplication
@MapperScan("com.edu.mapper")
public class EduApplication {
public static void main(String[] args) {
SpringApplication.run(EduApplication.class, args);
}
}
Mapper接口就变成了这样,基本啥都不用写:
java复制public interface CourseMapper extends BaseMapper<Course> {
// 单表CRUD直接继承,复杂查询加自定义方法
}
写一个复杂的联表查询时,可以用 @Select 注解或者XML方式。我项目的统计接口用了XML,因为SQL比较复杂,写在XML里面更好维护。MyBatis-Plus的 selectPage 做分页也很方便,配合PaginationInnerInterceptor插件,不用自己去拼 LIMIT。
这里说下判断分页的坑:如果用了MyBatis-Plus的分页插件,记得配置分页拦截器,否则 selectPage 不会生效,会查出来全部数据:
java复制@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
4. 前端交互与部署上线
4.1 前端页面规划
这个项目的技术栈是Vue 3 + Vite + Element Plus,没有用太复杂的状态管理库,只是一个学习类网站,用localStorage读一下用户信息就够了。
页面规划大概是这样:
- 首页:课程分类导航、热门课程、公告
- 课程列表页:按分类筛选课程
- 课程详情页:课程简介、章节目录、学习进度
- 章节学习页:文字内容、视频嵌入、下一章导航
- 在线测验页:选择测验、答题、提交、查看结果及解析
- 个人中心:学习记录、成绩记录
- 管理后台:数据概览、课程管理、题目管理、用户管理
用户端UI我做得比较简洁,重点在于“学得下去”。章节内容页面采用左侧章节目录、右侧正文的结构,类似文档站点的布局,用户翻页非常顺。测验页则是逐题渲染,提交后直接展示得分和错题解析。
4.2 前后端接口对接的核心注意点
前后端分离后,有几个细节必须处理好,否则联调时会很痛苦。
第一个是跨域配置。后端要允许前端开发服务器的跨域请求。我在SpringBoot里加了一个全局CORS配置:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
注意这里 allowCredentials(true) 和 allowedOriginPatterns("*") 要配合使用,在早期版本中 allowedOrigins("*") 和 allowCredentials(true) 同时设置会报错。
第二个是Token失效的处理。前端在axios拦截器里统一处理,如果返回的code是401,则跳转登录页并清除本地Token。前端代码大致这样:
javascript复制service.interceptors.response.use(
response => {
const res = response.data
if (res.code === 401) {
localStorage.removeItem('token')
router.push('/login')
}
return res
},
error => {
if (error.response && error.response.status === 401) {
localStorage.removeItem('token')
router.push('/login')
}
return Promise.reject(error)
}
)
第三个是文件上传。如果课程内容包含图片、PDF或视频,后端需要提供一个文件上传接口。我用的是SpringBoot自带的 MultipartFile,上传到本地 upload 目录,再映射成静态资源URL访问。生产环境当然应该用OSS,但校内项目本地文件方式完全够用,记得配置一下最大文件大小限制。
4.3 打包部署与常见坑
本地开发完成后,前后端是分开部署的:
- 后端:
mvn clean package打成jar包,扔到服务器上java -jar edu.jar启动。 - 前端:
npm run build生成静态文件,用Nginx托管。
Nginx配置里,关键是把 /api 开头的请求反向代理到后端端口:
nginx复制server {
listen 80;
server_name your-domain.com;
root /usr/share/nginx/html;
index index.html;
location /api/ {
proxy_pass http://127.0.0.1:8080/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location / {
try_files $uri $uri/ /index.html;
}
}
在这里我踩过一个坑:proxy_pass 后面如果带了 /api/ 路径,实际转发时会去掉匹配到的前缀。比如前端请求 /api/course/list,代理会把它转发成 http://127.0.0.1:8080/api/course/list,这里要看 proxy_pass 后面有没有带具体路径来决定是否保留前缀。我实际是直接配了带 /api/ 的写法,后端接口路径就是 /api/...,两边对齐就好。
后端打包时,还有个大坑就是JDK版本不匹配。Linux服务器上装的是JDK 8,本地是JDK 17,打出来的包在服务器上跑不了,报 UnsupportedClassVersionError。这个一定要在打包时指定release版本,Maven的 <java.version> 要按服务器实际环境来。
5. 常见问题与排查技巧实录
5.1 SpringBoot版本太高引发的连锁反应
这块我觉得非常有代表性。很多毕业设计是临时找的现成教材或者视频学的,视频里用的是SpringBoot 2.3,然后自己电脑上拉新项目默认生成的是SpringBoot 3.1,结果抄代码的时候各种花样报错:
javax包变成了jakarta包,import javax.servlet.*全部要改成jakarta.servlet.*- Springfox Swagger2不能用了,需要换成springdoc或者OpenAPI 3
- Redis的
JedisConnectionFactory变成了LettuceConnectionFactory
如果项目从零开始,我的建议很简单:要么统一用2.7.x走老教程的配置方式,要么统一用3.x按新文档来,千万别混着来。项目用的是SpringBoot 2.7.18,大部分老资料都兼容。你如果非要用3.x,遇到问题搜“Spring Boot 3 + 具体组件”这样的关键词,而不要直接抄2.x的答案。
5.2 前后端联调时常见的404和405
接口联调时,有些问题是HTTP层面的,而不是业务层面的。
404通常是路径不对。我用 @RequestMapping("/api/course"),前端请求 /api/course/list,但Controller里没有 @GetMapping("/list"),那就404。检查Controller路径注解和前端URL是否完全一致,尤其是大小写和末尾斜杠。
405是方法不对。前端用了 axios.post 发了POST请求,后端是 @GetMapping,自然报405。检查请求方法是否匹配。
还有一个常见坑是请求参数类型不匹配。@RequestParam 接收的是query参数,@RequestBody 接收的是JSON体,前端传JSON,后端用 @RequestParam 接,结果就是参数全部为null,业务逻辑直接报空指针。建议后端接口统一用DTO类接收参数,避免这种低级错误。
5.3 数据库乱码、时区与连接丢失问题
数据库相关的问题也是高频。乱码问题的根源在于字符集,MySQL建库时就必须明确指定utf8mb4:
sql复制CREATE DATABASE edu_site DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
连接串里的 characterEncoding=utf8 也要有,两边对齐后基本不会乱码。
时区问题除了连接串加 serverTimezone=Asia/Shanghai,MySQL服务端也可以设置默认时区。如果你用Docker起MySQL,启动时加参数:
bash复制-e TZ=Asia/Shanghai
Java代码里对时间字段我统一用 LocalDateTime,避免 java.util.Date 和前端时间格式不一致的麻烦。配合Jackson配置,序列化格式统一为 yyyy-MM-dd HH:mm:ss。
连接丢失这个问题,如果部署在服务器上长时间运行,经常会遇到凌晨起来发现系统提示“连接不可用”。这是连接池里的连接被MySQL服务端关闭了。解决方案是在数据源配置中增加:
yaml复制spring:
datasource:
hikari:
connection-timeout: 30000
validation-timeout: 5000
max-lifetime: 1800000
让连接池定期校验连接有效性,避免使用已经失效的连接。
5.4 前端页面空白、接口504这类“玄学”问题
如果你遇到前端部署后页面空白,先看Nginx的error.log,大概率是静态文件路径或者路由history模式的问题。使用history模式时,刷新二级页面会404,所以Nginx配置必须加上 try_files $uri $uri/ /index.html;。如果不用history模式,就用hash模式,URL带 #,不会有这个问题。
接口504需要看后端日志和请求时长。如果是某些统计接口特别慢,多半是SQL写法问题。比如用户学习进度统计,如果先查出所有课程再在循环里查每个课程的章节,数据量一大就慢。优化的思路是:一次SQL查出课程和用户进度的关联数据,用LEFT JOIN完成,避免循环查询。
我把常见问题整理成一个速查表:
| 现象 | 可能原因 | 快速解决 |
|---|---|---|
| 项目启动报依赖冲突 | 版本不兼容 | 统一SpringBoot版本,用mvn dependency:tree排查 |
| 登录接口返回401 | Token未传或过期 | 检查请求头Authorization,检查JWT有效期 |
| 接口返回中文乱码 | 数据库字符集或连接串问题 | 确认utf8mb4 |
| 查询数据为空 | 驼峰映射没开 | 开启map-underscore-to-camel-case |
| 上传文件失败 | 上传大小限制 | 配置MultipartResolver的maxFileSize |
| 分页失效 | 没有配置分页拦截器 | 添加PaginationInnerInterceptor |
| 生产环境跨域报错 | Nginx或CORS配置不对 | 统一处理跨域,最好用同域名部署 |
5.5 项目管理上的几个经验
代码写完后,有几件事很容易被忽略,但实际很影响最终评分和使用体验。
第一,接口文档。如果你是毕业设计,检查老师大概率会问你接口有哪些、每个接口怎么调。建议用Apifox或者Postman把所有接口整理好,导出文档,这样做整体规范性会好很多。我这边是在Apifox里建了一个项目,把20多个接口全部测过一边再导出的。
第二,初始化数据。系统部署上来不能是空库。我写了一个SQL初始化脚本,包含预设的管理员账号、3门计算思维课程、5门AI课程、每门课3-5个章节、每个章节10道测验题。这样系统一启动,输入账号密码就能演示全部功能。
第三,日志。开发环境把SQL打印出来很有用,可以盯着MyBatis生成的SQL排查问题。生产环境最好关掉SQL日志,改用logback按天滚动,避免日志文件无限增长。
第四,单元测试。SpringBoot项目最好给核心业务写一点单元测试,比如登录逻辑、判分逻辑。不需要覆盖全部,但基础的冒烟测试能帮你快速发现改了代码后把老功能弄坏的情况。我用SpringBootTest写了一套接口层测试,部署前跑一遍,心里踏实很多。
结语:做完这个项目的一点体会
如果你也要做这个题目,我个人的建议是:不要把重心放在“AI功能”有多炫上,而是把基础的学习闭环做扎实。计算思维和人工智能学习网站,本质上是一个内容型平台,用户注册、课程浏览、学习记录、在线测验、结果反馈,这条链路只要通了,系统就已经很完整了。AI的炫酷功能可以作为扩展点,比如在题目解析里引入大模型的接口来做智能答疑,但那是在基础功能之上锦上添花的事。
我在实际开发过程中最大的体会是,项目里遇到的绝大多数问题都不是什么新鲜问题。报错了就先看日志,日志看不懂就搜索,搜索不到就去读源码注释。SpringBoot的社区非常成熟,你踩过的坑几乎都有人踩过。把这个项目老老实实从头到尾做完,你对SpringBoot的理解会有一个质的飞跃——那种“框架只会用配置,出了问题就懵”的状态,基本就消退了。
最后再分享一个小技巧:版本管理一定要用Git,从项目第一天开始就初始化仓库,每完成一个功能就提交一次。我做完后看提交记录,能清晰看到自己每天的进度。万一某天改崩了,也能随时回退。别嫌麻烦,到后期你就知道这个习惯有多值钱了。
