先聊点实在的。很多打算做毕业设计的同学看到“Spring Boot课程资源在线销售系统”这个题目,第一反应是“又是一个老掉牙的商城项目”,但真上手做的时候才发现,它比普通电商项目多出了几个关键的设计难点:课程资源的权限控制怎么搞?虚拟商品的“发货”逻辑是什么?订单状态怎么管才能既简单又不容易出错?这些点恰恰是答辩时最能拿得出手的“技术增量”。这篇文章我就以实际带项目的视角,把这个题目的需求拆解、技术选型、数据库设计、核心编码和踩坑实录完整捋一遍,想用这个题目做毕设的同学可以直接照着动工。
1. 项目全景解读:课程资源在线销售系统到底在做什么
1.1 从业务角度理解系统价值
课程资源在线销售系统,说直白一点,就是个卖课和卖学习资料的垂直电商平台。商品是视频课程、PDF文档、代码工程包这些虚拟资源,买家是学生或在职学习者,卖家可以是平台本身,也可以是注册的讲师、创作者。这种系统在真实行业里到处可见,比如各类知识付费平台、题库平台,但作为毕业设计,它的定位更侧重于“一套完整的电商闭环”+“虚拟资源特有的权限控制”。
从业务闭环来看,系统至少需要覆盖:用户注册登录、浏览课程列表、按分类或关键词检索、课程详情展示、加入购物车、生成订单、模拟支付、支付成功后自动解锁课程资源、个人已购课程列表、后台的课程管理和订单管理。这个闭环做完整了,业务逻辑是自洽的,答辩时讲起来也很有层次感。
为什么要用“课程资源”而不是“普通商品”作为系统核心?我个人的理解是,普通商城项目(比如卖衣服、卖数码产品)涉及的物流、库存、售后这些模块对毕设来说太重了,而且容易陷入“全是增删改查”的窘境;而虚拟资源销售天然规避了物流问题,把核心矛盾集中在“订单状态流转”和“资源访问权限”上。这两个点恰好是面试和答辩时最能展示业务理解深度的地方。
1.2 毕业设计的切入点与评分亮点
我用下面这张表来梳理这个题目在毕设评分维度上的得分点分布:
| 评分维度 | 系统对应的亮点 | 设计难度 |
|---|---|---|
| 需求完整性 | 前后台分离,用户、商品、订单、支付、权限全部覆盖 | 中等 |
| 技术深度 | JWT鉴权、拦截器、Redis缓存、事务控制、定时任务 | 中等偏上 |
| 业务复杂度 | 虚拟商品自动解锁逻辑、订单状态机、支付模拟回调 | 较高 |
| 工程规范性 | 分层架构、统一返回结果、全局异常处理、日志埋点 | 中等 |
| 创新可扩展性 | 可以加推荐算法、验证码、微信扫码模拟支付等 | 灵活 |
从这张表能看出,这个题目的“性价比”很高。它不会像纯管理系统那样被评委批“没有技术含量”,又不会像大型分布式项目那样让你在三个月内疲于奔命。只要你把订单流转和资源权限这两条主线做扎实,再把工程化的几个细节——统一返回、异常拦截、参数校验——做到位,拿到一个不错的分数是大概率事件。
2. 技术选型解析:为什么它的核心是Spring Boot但又不只是Spring Boot
2.1 后端框架选型的真实思考
题目点名了Spring Boot,这本身就是一种技术选型倾向。Spring Boot最大的价值在于“约定优于配置”:内置了Tomcat、自动装配了各种starter、提供了统一的配置管理,让你在极短时间内搭起一个可运行的项目骨架。相比传统Spring + Spring MVC + XML配置的方式,Spring Boot把程序员从繁琐的配置文件中解放出来,这对于毕业设计来说非常友好,因为你的精力应该花在业务逻辑上,而不是花在“为什么这个Bean注入不进去”上。
但用户管理、订单、商品这些模块,单靠Spring Boot的Web能力是不够的。完整的后端技术栈还需要:
- Spring Security + JWT:做登录认证和接口访问控制。JWT的好处是服务端无状态,前端(如果做前后端分离)拿到token后每次请求带在请求头里即可,不需要像Session那样考虑跨域共享问题。
- MyBatis-Plus:操作数据库的持久层框架。它在MyBatis基础上封装了通用Mapper和条件构造器,单表CRUD几乎不用手写SQL,可以省下大量时间。多表关联查询再手写XML,两者结合效率最高。
- Redis:缓存热门课程列表、课程详情、验证码等数据。毕业设计中不要求Redis用得多么精深,但至少要在课程详情缓存、订单防重复提交这两处用上,技术含量立刻不一样。
- Spring Validation:做参数校验。不要在前端校验完就完事,后端接口必须有这一层,否则答辩时会被问“非法参数怎么拦截”这类问题。
- Lombok:简化实体类代码,减少getter/setter的样板代码。
2.2 前端方案的取舍
前端我推荐两个方向,根据你的时间精力二选一。
方向一:经典方案,Thymeleaf模板引擎 + Bootstrap + jQuery。后端渲染页面,前后端不分离。这种方案的好处是技术栈简单、调试方便、部署时不涉及跨域问题,适合前后端能力都一般、想尽快跑通流程的同学。
方向二:前后端分离,Vue 3 + Element Plus + Axios,后端提供JSON接口。这种方案的好处是更贴近现在工业界的开发模式,前端页面效果更加现代、美观,答辩展示时视觉冲击力更强。但代价是你需要额外维护一套前端工程,还要处理跨域配置、token存储、路由守卫这些问题。
我个人的建议是:如果你的毕设时间充足(三个月以上),优先选前后端分离;如果只剩一个月时间又想稳妥,那就老老实实用Thymeleaf。实际上,当年我带过一个学生,他就是先用Thymeleaf把功能全部跑通,后来觉得页面太朴素,再用Vue重写了前端页面,后端接口几乎没怎么大改,因为我在设计后端时就专门把Controller层限定为JSON返回,为的就是这种扩展可能。
2.3 数据库选型与核心表设计
数据库直接用MySQL 8.x,字符集选utf8mb4,排序规则用utf8mb4_general_ci。下面这张是我认为比较合理的基础表结构:
| 表名 | 主要字段 | 作用说明 |
|---|---|---|
| user | id, username, password, nickname, role, email, avatar, status | 用户表,role区分管理员/讲师/普通用户 |
| course | id, title, subtitle, cover, price, original_price, category_id, teacher_id, description, status, sales_count, score, create_time, update_time | 课程表,status控制上架/下架 |
| course_section | id, course_id, title, sort, duration, video_url, is_free | 课程章节/课时表,支持试看 |
| resource_download | id, course_id, file_name, file_url, download_count, create_time | 课程附属下载资料(代码包、文档) |
| category | id, name, parent_id, sort | 分类表(Java、前端、Python、设计等) |
| cart | id, user_id, course_id, quantity, create_time | 购物车表,课程商品数量恒为1 |
| orders | id, order_no, user_id, total_amount, pay_amount, status, pay_time, cancel_time, create_time | 订单主表 |
| order_item | id, order_id, course_id, course_title, course_cover, price | 订单明细表 |
| user_course | id, user_id, course_id, acquire_time, source | 用户课程关系表,支付成功后写入 |
设计这张表时有一个关键点:为什么需要order_item这层明细表?因为订单表本身是面向“一次支付行为”的聚合记录,而一个订单可能包含多门课程,课程的信息(标题、封面、价格)又可能在购买后被讲师修改,所以必须把商品快照存到明细表里。这个设计思路能体现你对数据库范式和数据历史可追溯性的理解,答辩时别在这个细节上栽跟头。
另外,user_course表是整个系统“虚拟商品权益”的核心。用户有没有权限查看某门课程,不看订单状态,就看这张表里有没有记录。这样设计的好处是,即使订单被退款或删除,也不会影响用户的已购权益纠正流程——只要user_course还在,权益就在。
3. 核心业务模块拆解与实现思路
3.1 用户认证与权限体系
用户体系我用的是Spring Security + JWT的组合,但不用Spring Security的完整过滤器链,而是简化处理:登录成功后签发一个JWT token,前端每次请求在Authorization头里带上这个token,后端写一个拦截器来解析token并获取当前用户信息。
具体来说,UserDetailsService实现类负责根据用户名查库并校验密码,密码使用BCrypt加密存储。签发token时把用户id和角色作为payload写进去,expiration设置成两小时过期。拦截器里读取token、解析出用户id、放到ThreadLocal里方便后续业务代码获取当前登录用户。
这里有个非常重要的设计细节:为什么不用原生的Session?因为如果你的前端和后端跑在不同端口上(比如前端8080、后端9090),Session天然会遇到跨域Cookie携带问题,处理起来相当烦人。JWT方案则没有这个困扰,前端把token存到localStorage,Axios拦截器统一加到请求头里即可。需要注意的是,JWT是无状态的,一旦签发,在过期之前是无法从服务端主动作废的,所以如果需要“强制退出”“禁用用户”这类功能,得配合Redis做一个token黑名单机制。毕设如果只做基础功能,可以在改用户状态时强制要求重新登录,或者将token有效期缩短。
3.2 课程资源管理与权限控制
课程资源管理的核心难点不在于“上传文件”本身,而在于“如何保护资源”和“如何发放资源”。
上传方面,如果资源文件不大(单文件50MB以内),本地存储是够用的。在配置文件中设置一个文件上传路径属性,用UUID重命名文件以避免文件名冲突,然后把文件路径存到数据库。如果课程视频动辄几百MB,本地存储就有点吃力了,这时候更稳妥的方式是接入云存储,把文件传给OSS,拿到URL存放在数据库里。但云存储需要涉及密钥配置,答辩展示时要提前准备好网络环境,避免现场翻车。
保护资源方面,我的建议是不要把真实视频地址直接暴露给前端。比较简单的做法是:视频播放接口都加上登录校验,单个课时接口还要校验当前用户是否拥有该课程权限。对于配套的下载资源(如代码工程包),也走专门的下载接口,在接口层面校验登录和购买状态,用流的方式输出文件内容,而不是把存储路径明晃晃写在页面里。
发放资源方面,就是最核心的“下单后自动解锁”。支付成功回调后,在同一个事务里完成两件事:更新订单状态为已支付;往user_course表插入用户和课程的关联记录。事务保证这两步同时成功或同时失败,避免出现订单显示已支付但用户没权限访问课程的脏数据。
3.3 购物车与订单流程
购物车模块相对简单,因为虚拟商品没有库存概念,数量恒为1,所以购物车表甚至可以不设计quantity字段。核心操作就是三件事:加入购物车(防重复加入)、查看购物车列表、勾选结算(批量下单)。
订单模块是系统里最容易出问题的地方,我把它的状态流转列出来:
待支付(0) -- 用户支付 --> 已支付(1) -- 管理员/系统 --> 已完成(2) 待支付(0) -- 用户取消 --> 已取消(3) 待支付(0) -- 超时未付(定时任务) --> 已关闭(4)订单创建时生成唯一的orderNo,格式可以用日期+随机数+用户id片段拼接,比如20250512001。为什么订单号不能直接用数据库自增id?因为在真实场景中,订单号需要在一定程度上防止他人猜测、需要支持跨系统追踪,而且脱离开自增id的主键规律性能更有保障。毕设阶段不需要搞雪花算法那么复杂,一个兼顾可读性和唯一性的自定义规则就足够了。
订单创建后,用户点击“去支付”,进入模拟支付页面。这里有一个业务设计上的取舍:真实支付回调是支付平台反向请求服务器通知支付结果,而模拟支付可以简化成“用户点击确认支付 -> 前端请求支付接口 -> 后端直接处理订单状态流转”。我推荐用一个接近真实的模型:前端跳转到模拟收银台页面,点击“确认支付”后,前端先调用“发起支付”接口,后端返回一个模拟支付凭证,然后前端再用这个凭证调用“支付结果确认”接口,后端在这个接口里统一完成“校验金额 -> 更新订单状态 -> 写入user_course -> 扣减讲师收益余额”这一串事务性操作。这样流程分了两步,更接近真实支付网关的回调语义,答辩时也能讲清楚。
3.4 购物车与订单的并发一致性设计
毕业设计通常不用考虑特别高并发的场景,但有一个问题必须提一下:超卖和重复支付。虽然虚拟商品没有库存,但用户连续点击两次“确认支付”,就会触发两个事务同时对同一个订单做状态流转。
解决方案有两种思路。
第一种,数据库层面的乐观锁。在orders表加一个version字段,更新订单状态的SQL写成:
UPDATE orders SET status = 1, pay_time = NOW(), version = version + 1 WHERE order_no = #{orderNo} AND version = #{version}判断受影响行数,如果是0说明订单状态已经被别人改过了,直接返回“订单已处理”。
第二种,应用层的分布式锁。用Redis的setnx命令在用户维度加锁,比如key为lock:order:{orderNo},只有在成功获取锁的请求才能处理订单。这个方案实现起来稍微复杂一些,但可以顺手把Redisson或者StringRedisTemplate的用法展示出来,也是加分项。
我的建议是两者结合:代码里先用Redis锁做请求级别的拦截,数据库更新时再加乐观锁兜底。你不需要在毕设里展示多高深的并发技术,但能说出这两层保障的“组合拳”设计,已经是相当不错的工程素养了。
4. 实操过程:从零开始搭建项目骨架到跑通全流程
4.1 环境准备与项目初始化
开发环境建议如下:
- JDK 1.8 或 JDK 11(用什么版本取决于你在学校课程里接触过的版本,如果是跟视频课学的,就保持和视频一致,避免高版本踩坑)
- Maven 3.6+
- MySQL 8.0
- Redis 6.x(需要安装并启动,窗口别关)
- IDEA 2023及以上版本
初始化项目时,我建议直接去Spring Initializr生成默认工程,依赖选上:Spring Web、Spring Security、MyBatis-Plus、MySQL Driver、Validation、Lombok、Redis。生成完把配置写好:
spring: datasource: url: jdbc:mysql://localhost:3306/course_sale?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 你的密码 redis: host: localhost port: 6379 server: port: 8081 mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开发时打印SQLmybatis-plus的map-underscore-to-camel-case一定要开,否则数据库字段course_title映射不到实体属性courseTitle。log-impl打印SQL这个配置在开发阶段极其有用,出现数据库问题时能直接看到执行的SQL语句。
4.2 实体类、统一返回结果与全局异常处理
实体类用MyBatis-Plus注解配合Lombok来写:
@Data @TableName("course") public class Course { @TableId(type = IdType.AUTO) private Integer id; private String title; private String subtitle; private String cover; private BigDecimal price; private BigDecimal originalPrice; private Integer categoryId; private Integer teacherId; private String description; private Integer status; private Integer salesCount; private Float score; private LocalDateTime createTime; private LocalDateTime updateTime; }这里有个细节,课程表里存的是teacherId(讲师用户id),前端展示时总是要显示讲师姓名。两种处理方式:要么在查询课程时关联user表查出nickname,要么在Course实体里加一个瞬时字段teacherName,用@TableField(exist = false)标记它不是数据库字段。我推荐瞬时字段的方式,因为课程列表和详情页都需要这个字段,关联查询写起来还省事。
统一返回结果类Result 也是必备的。我的定义:
public class Result<T> { private Integer code; private String message; private T data; public static <T> Result<T> success(T data) { ... } public static <T> Result<T> error(String message) { ... } }全局异常处理器用@RestControllerAdvice + @ExceptionHandler,把业务异常、参数校验异常、兜底异常分别处理,统一返回Result格式。这样Controller不会到处都是try-catch,前端的错误提示也会友好得多。
4.3 购物车、订单与支付核心编码细节
购物车加入接口:
@PostMapping("/cart/add") public Result<Void> addCart(@RequestBody AddCartRequest request, @CurrentUser User user) { // 1. 查询课程是否存在且是上架状态 // 2. 查询购物车中是否已有该课程,有则直接返回“已在购物车” // 3. 插入购物车记录 }@CurrentUser这个注解是自定义的,配合HandlerMethodArgumentResolver从ThreadLocal里取当前登录用户,这样代码读起来特别清爽。
订单创建接口是核心中的核心:
@Transactional public OrderNoResult createOrder(Integer userId, List<Integer> courseIds) { // 1. 查询课程列表(务必使用in查询,避免N+1问题) // 2. 计算总价 // 3. 生成订单号 // 4. 插入orders表 // 5. 批量插入order_item表,保存课程快照 // 6. 清空用户购物车中对应的课程 // 7. 返回订单号给前端 }注意:总额计算必须从数据库实时查询,不能信任前端传来的金额,否则用户可以随便改请求里的价格字段。这是虚拟商品交易系统最容易出现的安全漏洞,一定要在答辩时主动提出来。
支付确认接口:
@Transactional public PaymentResult confirmPay(String orderNo, Integer userId) { // 1. 用Redis锁锁定orderNo // 2. 查询订单,校验归属权和状态是待支付 // 3. 更新订单状态为已支付,更新支付时间 // 4. 写入user_course(遍历订单明细表里的课程) // 5. 增加课程的销售数 // 6. 生成模拟支付流水记录 }第3步和第4步必须在同一事务下执行。由于项目使用的是默认的Spring事务,只需要给这个Service方法加上@Transactional并在启动类上开启@EnableTransactionManagement即可。
4.4 定时任务关闭超时订单
用户点击创建订单后如果一直不支付,订单就一直占着“待支付”状态。合理做法是设定15分钟未支付自动关闭。实现方式用Spring自带的@Scheduled注解:
@Component public class OrderTimeoutTask { @Scheduled(cron = "0 */1 * * * ?") public void closeExpiredOrders() { // 查询创建时间超过15分钟且状态为待支付的订单列表 // 批量更新状态为已关闭 } }启动类上加@EnableScheduling,这是整个系统里最轻量级的“主动任务”实现。不引入Quartz是因为杀鸡不用牛刀,但如果你有精力,把Quartz集成进来,展示任务调度能力,也是一个加分方向。这里有一个待办的细节点:订单超时关闭之后,购物车里对应的课程记录其实不需要恢复,因为购物车只需要在创建订单时顺手清空即可;如果用户还想买,他再去购物车重新添加、重新下单,这是多数电商平台的常见交互,并不影响业务正确性。
5. 常见问题与排障实录
5.1 分页查询的坑:前端页面没数据,后端却报PageHelper的错
如果你用MyBatis-Plus的Page对象做分页,注意必须在Mapper接口里传入IPage参数或在Service里用Page对象来查询,并且如果有自定义SQL,必须用selectPage或者XML里的<select>标签来包裹分页逻辑。一个常见的报错是“JSQLParserException: Failed to process expression”,这多半是因为自定义SQL的JOIN写法或者子查询过于复杂,导致MyBatis-Plus的分页插件无法解析。解决办法分两种:要么简化JOIN,把复杂查询拆成多个简单查询然后在Service层组装;要么仍然采用PageHelper做手动分页。这里沉下心来踩过一次坑之后,就能理解“分页插件是好东西,但不是万能的东西”。
5.2 Jackson序列化懒加载对象报错
课程详情接口通常要返回课程信息,同时需要带出讲师姓名。如果不小心把课程和讲师的关系配置成了JPA的懒加载(虽然MyBatis-Plus没有JPA这种概念,但很多人会把课程的teacherId关联查询写到ServiceImpl里,关联出来的对象在POJO转JSON时就可能报懒加载或序列化失败)。解决思路很直接:
- 方案一:将所有关联查询的结果直接投射成VO,不返回数据库实体。
- 方案二:在实体中的冗余字段(如teacherName)里填充好值,Controller直接返回实体即可。
最忌讳的做法是在Controller层突然调用某个getter去触发懒加载,那样只能在调试器里看到数据,一到JSON序列化就崩溃。
5.3 文件上传大小限制
Spring Boot默认单个文件上传上限是1MB,超出会报MaxUploadSizeExceededException。我见过很多同学项目都上线了自己电脑,结果传个课程封面图片都一直失败,就是因为没配置上传限制。在yml里加上:
spring: servlet: multipart: max-file-size: 100MB max-request-size: 200MB如果你上传的是视频,就要考虑超过100MB的情况,那更建议先做分片上传等技术,而不是简单调大限制。对毕设而言,建议课程视频能以链接的形式填写,把视频文件本身放在另一个静态资源目录或者短视频平台,这样既能简化代码,也避免答辩现场传大文件的尴尬。
5.4 并发下单时的超卖和状态错乱
重复请求导致订单状态错乱,前面已经提了Redis锁+乐观锁的双重方案。这里再给出一个我在实际项目里会用到的模板代码:
// Redis锁模板 String lockKey = "lock:order:" + orderNo; Boolean locked = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "1", Duration.ofSeconds(10)); if (Boolean.FALSE.equals(locked)) { throw new BizException("操作太频繁,请稍后再试"); } try { // 业务逻辑 } finally { stringRedisTemplate.delete(lockKey); }注意锁的过期时间设置,如果业务执行时间有可能超过过期时间,兜底方案是给锁续期,但这个对毕设来说不要求。核心是演示出你意识到并发场景下状态判断的必要性。
5.5 前后端联调时跨域配置
如果用了前后端分离,跨域是绕不开的。在后端配置CorsFilter或@CrossOrigin,建议在配置类里统一配置:
@Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.addAllowedOriginPattern("*"); config.addAllowedHeader("*"); config.addAllowedMethod("*"); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } }特别提醒:如果开启了allowCredentials(true),allowedOrigin不要写成*,必须用addAllowedOriginPattern("*"),否则浏览器会有安全策略报错。这是很隐蔽的一个坑,报错信息不直观,很多同学会莫名其妙折腾半天。
6. 答辩准备与项目扩展建议
6.1 答辩演示的“三步走”策略
答辩时间通常只有10到15分钟,怎么在这么短时间里把项目亮点讲清楚?我给学生的建议是“三步走”:先讲背景和痛点,再讲技术架构和数据库设计,最后走一遍核心用户流程。具体来说:
- 痛点环节,30秒:说清楚“课程资源在线销售系统要解决在线买课难、课程版权控制不易、订单发货繁琐这三个问题”。
- 设计环节,3分钟:展示技术架构图(画图工具里画好,PPT上呈现),重点说明Spring Boot的分层架构、JWT权限控制、Redis缓存、订单状态机设计。
- 流程演示,5分钟:按“注册/登录 -> 浏览课程 -> 搜索 -> 详情 -> 加购物车 -> 下单 -> 支付 -> 课程立即解锁 -> 学习课程 -> 下载资料 -> 讲师发布新课程 -> 管理员上下架”这十一个步骤走一遍。
另外一定要准备好一个应急静态页:如果现场网络出问题或Redis没启动,至少还有截图或录屏兜底,这是无数次被人问“能不能提前录个视频”换来的教训。
6.2 功能扩展的进阶方向
毕设基础上可以再往上叠加的亮点,我按性价比从高到低排序:
- 课程评价与评分:下单并学习后,用户可以对课程评分、写评价。这是给系统增加“UGC氛围”最便宜的一招。
- 讲师收益统计:在讲师端增加一个仪表盘页面,展示已发布课程总数、累计销售额、各课程订单趋势。这看起来很唬人,其实就是一个分组统计SQL的事。
- 搜索关键词高亮:用MyBatis-Plus的like查询配合前端高亮展示,能走完“搜索—结果—详情”的完整链路。
- 数据报表:集成ECharts做后台首页的折线图(近7日销售额)、饼图(课程分类占比)。一张图顶得上十页文字。
- 验证码登录或邮箱找回密码:把安全体系补全,答辩时老师喜欢问“你的系统怎么保证安全”,这个功能能直接回应。
至于更高级的推荐算法(协同过滤)、消息队列削峰、分布式事务这些,就是纯粹展示个人余力的加分项,做不出来不影响基础分,做出来了就是降维打击。但请记住前提:先把主流程搞扎实,别为了炫技把基础功能丢了。
6.3 一对一的避雷清单
最后再分享一份我在多次带项目过程里总结出来的答辩避雷清单:
- 千万不要说“这个功能我是参考XX网课做的”——可以说“这个思路借鉴了主流的电商方案,但是结合了虚拟资源商品的属性做了调整”。
- 千万不要只展示页面,忽略数据库的字段设计依据。老师最喜欢翻数据库表,你要能顺手解释为什么orders表和order_item表要拆开、为什么引入user_course表来管权益。
- 千万不要把密码明文存在数据库里。只要被问到安全,BCrypt加密先行。
- 千万不要隐瞒Bug。被问到系统有什么不足时,可以直接承认“目前对恶意下载没有做防盗链处理,URL有效期较短,这是我后续的优化方向”——坦诚永远是答辩的通关密码。
我个人带过好几届学生做电商类毕设,最深的体会是:这个题目真正的分水岭,不在用了多少新技术,而在你对你设计的业务闭环有没有想清楚。很多同学做完连“为什么支付成功要写user_course表”都回答不明白,那才是真正的失分点。而反过来,哪怕技术栈朴朴素素,只要思路清晰、事务处理正确、安全意识到位,答辩老师普遍会给出不错的评价。如果这篇文章帮你把业务主线和几个关键技术点整明白了,那这个项目就已经成功了一大半。剩下的就是拿起电脑,把第一个Spring Boot入口跑起来。