最近好多读者私信我同一个问题:网上那些标题写着“基于Spring Boot的XX电商平台系统 源码+论文+答辩PPT”的项目,到底能不能直接拿来用?尤其是这个“小米电商平台系统”的题目,在各大资源站、毕业设计清单里出现的频率实在太高了。我的看法是:源码可以拿,但如果你只想下载、解压、点运行,然后指望答辩时靠它过关,大概率会在评委问出第一句“你这个购物车为什么用Redis”的时候当场卡壳。
这篇博文,我想用带实际项目复盘的口吻,把这个所谓“基于Spring Boot的小米电商平台系统设计与实现”掰开揉碎讲清楚。它本质上是一个标准的前后端分离教学型电商系统,核心技术栈就是Spring Boot + MyBatis-Plus + Redis + MySQL,前端通常是Vue或Thymeleaf,涉及用户注册登录、商品展示、购物车、下单、订单管理、支付回调模拟等完整电商闭环。适合谁看?正在做毕业设计、找工作需要放项目经验、或者想快速上手一个“能跑、能讲、能扩展”的Java后端项目的同学。看完你不仅能跑通项目,还能理清每个模块为什么这么设计,论文怎么写,PPT怎么讲,以及评委最喜欢从哪个角度追问。
1. 项目是在解决什么问题——需求拆解与设计思路
1.1 一个“电商平台”项目隐含的标准业务闭环
先别急着打开IDEA,第一步应该是问自己:这个系统到底要做什么?电商平台听起来很大,但落到毕业设计或简历项目,核心其实是几条链路。
最基础的一条是用户链路:注册、登录、个人信息维护。绝大多数项目都会用JWT(JSON Web Token)做无状态登录,用户名密码登录后服务端签发一个Token,前端存起来,后续请求带上就行。这里有个隐藏考点:密码不能明文存数据库,要走BCrypt加密;Token要有过期时间,甚至需要Refresh Token机制。很多同学下载的源码里根本没做加密,密码直接varchar存明文,这种细节在答辩时被问到就是送分题变送命题。
第二条是商品链路:商品分类、商品列表、商品详情、商品搜索。这个模块看起来简单,但可以玩的花样不少。商品列表通常要分页,搜索可以选择用MySQL的like模糊查询,也可以引入Elasticsearch做更专业的全文检索。对于毕业设计来说,用MySQL加Redis缓存已经是够用的方案,关键是你得能解释清楚缓存什么时候失效、数据一致性怎么保证。
第三条是交易链路:购物车加入、购物车修改、下单、订单支付、订单状态查询。这是整个系统最核心也最容易出问题的地方。购物车的实现方式有几种:用Session存、用数据库存、用Redis存。用Session的好处是实现简单,但服务端重启数据就没了,且不利于横向扩展;用数据库存是最稳妥的方案,但每次增删改查都打数据库;用Redis存是性能和功能折中最好的方案,key可以用用户ID,value用商品ID加数量,还能设置过期时间兜底。
下单环节更要命:要校验商品库存、要锁定库存、要生成订单号、要扣减库存,同时还要保证这些操作是原子的。如果代码里是“先查询库存,if库存>0,再update库存”,高并发下必然超卖。这个问题几乎每个电商项目都会被问到,也是展示你水平的最佳切入点。
第四条是管理端链路:管理员登录、商品上架下架、订单发货、用户管理。说白了,就是给系统配一个后台管理界面,让整个系统看起来更完整。很多项目源码里管理端做得比较粗糙,但你在论文里至少要写清楚权限怎么控制(比如用拦截器判断角色)。
1.2 为什么选Spring Boot而不是其他技术栈
这个问题看起来没有悬念,但你要能说出理由,而不只是“因为大家都在用”。Spring Boot的核心价值是“自动配置”加“生态成熟”。
自动配置解决的是传统Spring项目里大量XML配置的问题。以前做一个SSH(Struts+Spring+Hibernate)项目,光配置文件就能写几十行,还不一定一次写对。Spring Boot通过starter依赖和自动配置,把常见的场景(Web、数据访问、缓存、消息队列)都预设好了,你只需要引入依赖并在application.yml里写少量配置。
生态成熟就更直观了。就拿电商系统需要的功能来说:登录认证有Spring Security或Sa-Token,数据访问有MyBatis-Plus或Spring Data JPA,缓存有Spring Data Redis,定时任务有Spring Task或Quartz,接口文档有Knife4j或Swagger。这些组件都是久经考验的,遇到问题搜索一下基本都有解决方案,对新手极其友好。
相比其他方案:Python的Django/FastAPI学起来简单,但Java岗位市场需求量更大;PHP做电商很快,但面试时技术含金量偏低;Go的Gin框架性能好,但生态和岗位数量还不如Java。所以如果你的目标是“做完项目能写进简历并找到一份后端开发工作”,Spring Boot几乎是标准答案。
1.3 功能边界的确定:不要把毕业设计做成创业项目
这是我最想强调的一点。很多同学做这类题目时容易失控,今天加个秒杀模块,明天加个优惠券系统,后天又想做直播带货后台,最后代码写了一万多行,bug多到改不完,论文却没时间写。
合理的方式是做减法。以小米电商平台系统为例,核心功能就锚定在:用户端能够注册登录、浏览商品、搜索商品、加入购物车、提交订单、模拟支付、查看订单;管理端能够管理商品分类、管理商品、处理订单状态。能做到这些,项目已经足够完整。
至于秒杀、优惠券、推荐系统、聊天客服,这些可以作为“扩展与展望”写进论文的最后一章,说明“系统预留了扩展接口,未来可以接入Redis分布式锁实现秒杀”之类的话。答辩时这样讲反而是加分项,因为说明你有全局设计意识,而不是盲目堆功能。
我见过一个反面案例:某同学在系统里接了支付宝沙箱支付、接了腾讯云短信验证码、还用WebSocket做了客服聊天,结果演示到一半,短信服务欠费、支付回调没配好、WebSocket断线重连报错。评委当场皱眉,论文也写得像流水账。所以,功能精而深,永远比多而浅更有说服力。
2. 核心模块与数据库设计:先把数据的根扎稳
2.1 用户与商品模块设计要点
数据库设计是论文里最直观的“设计实现”体现,评委必看ER图。所以表设计不能含糊,至少要符合第三范式(3NF),但也要允许适当冗余来提升查询性能。
用户表(user)常用字段大概有:id、username、password(BCrypt加密后的密文)、nickname、avatar、phone、email、gender、status(是否禁用)、create_time、update_time。这里有个细节:数据库字段名建议用下划线风格(create_time),Java实体里用驼峰风格(createTime),然后通过MyBatis-Plus的map-underscore-to-camel-case配置自动映射,这是最常规也最不容易出错的约定。
商品分类表(category)字段:id、parent_id(支持二级分类)、name、sort、icon、status。商品表(product)字段:id、category_id、name、sub_title、main_image、detail、price、stock、sales、status(0下架1上架)、create_time、update_time。有一个字段容易被忽略:detail(商品详情),它长什么样直接影响前端页面。如果只是纯文本,前端渲染非常简单;如果存的是富文本HTML,前端需要用v-html渲染,还要注意XSS攻击风险。
商品图片这块,很多源码里存的是一个URL,比如https://xxx.com/image/1.jpg。但本地开发时,如果图片服务器没配好,页面就会破图。比较稳的做法是把图片放在项目的static目录或配置一个本地资源映射路径,比如/upload/**映射到服务器的某个磁盘路径。
2.2 购物车与订单模块:状态机的价值
购物车表(cart)如果采用数据库存储,字段建议是:id、user_id、product_id、quantity、checked(是否勾选)、create_time、update_time。加一个user_id和product_id的联合唯一索引,防止同一用户反复添加同一商品时出现重复数据。更合理的是每次加入购物车时先查询是否已存在,存在则更新数量,不存在则新增。
订单模块是设计的重头戏。订单主表(orders)字段建议:id、order_no(订单号)、user_id、total_amount、pay_amount、freight、status、pay_time、delivery_time、finish_time、create_time。订单明细表(order_item)字段建议:id、order_id、order_no、product_id、product_name、product_image、price、quantity、total_price。为什么要把商品快照(名称、图片、价格)冗余到明细表里?因为订单生成之后,商品可能会改名、改价、下架,但订单里的信息必须保持当时成交时的样子。这个点面试和答辩都会问,必须能答上来。
订单状态字段status是一个典型的状态机模型。常见的状态流转是:0待支付 -> 1已支付(待发货) -> 2已发货 -> 3已完成,另外还有4已取消、5退款中、6已退款。状态之间不能随便跳转,比如已发货的订单不能直接改成已取消,要先走退款流程。在Java代码里,可以用if判断或者更优雅的策略模式来管理流转,但毕业设计用if判断已经足够了,关键是你要能解释清楚为什么状态不能乱跳。
2.3 核心表结构速查与字段设计思路
为了让你心里有底,我整理了一张最简版本的表结构清单。这个清单是教学项目常规方案的补充,适合先搭骨架,再按自己论文需要微调。
| 表名 | 用途 | 关键字段 | 备注 |
|---|---|---|---|
| user | 用户表 | id, username, password, nickname, phone, status | password存BCrypt密文 |
| category | 商品分类表 | id, parent_id, name, sort | 支持二级分类 |
| product | 商品表 | id, category_id, name, price, stock, sales, status | 列表页状态过滤 |
| cart | 购物车表 | id, user_id, product_id, quantity, checked | 联合唯一索引 |
| orders | 订单主表 | id, order_no, user_id, total_amount, status | 订单号唯一索引 |
| order_item | 订单明细表 | id, order_id, product_id, product_name, price, quantity | 冗余商品快照 |
| address | 收货地址表 | id, user_id, receiver, phone, province, city, detail | 默认地址is_default |
| admin | 管理员表 | id, username, password, role | 后台登录 |
另外,现在几乎所有Spring Boot电商项目都会引入Redis。Redis在项目里的典型用途是:缓存商品分类列表、缓存首页轮播图、缓存商品详情、存储购物车数据、存储验证码。你写论文的时候,可以在“系统设计”部分画一张Redis存储结构图,key, value的格式都写清楚,答辩时直接照着讲,非常加分。
3. 关键链路实现与代码落地
3.1 用户登录与JWT拦截器
登录模块是面试官最爱“深挖”的模块,因为每个人都会做,但很少有人能把细节讲清楚。标准的流程是:前端把用户名密码传到后端,后端校验BCrypt密码,校验通过后用用户的id和用户名生成JWT Token,返回给前端。前端拿到Token后存到localStorage或pinia中,后续请求在HTTP Header里带上Authorization: Bearer {token}。
后端需要写一个拦截器(HandlerInterceptor)或者过滤器(OncePerRequestFilter)来统一校验Token。这里有几个易错细节:放行路径要配好,比如登录接口、注册接口、商品列表接口可以不校验,但购物车、下单、订单查询这些接口必须校验;Token解析失败时要返回401状态码,而不是直接抛500;解析出用户id后,可以用ThreadLocal存储当前用户信息,方便后续业务代码直接获取。
实际项目中,我是这样组织代码的:
@Component public class LoginInterceptor implements HandlerInterceptor { @Autowired private StringRedisTemplate redisTemplate; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行预检请求 if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { return true; } String token = request.getHeader("Authorization"); if (StringUtils.hasText(token) && token.startsWith("Bearer ")) { token = token.substring(7); try { Claims claims = Jwts.parserBuilder() .setSigningKey(secretKey) .build() .parseClaimsJws(token) .getBody(); Long userId = Long.valueOf(claims.get("userId").toString()); UserContext.set(userId); return true; } catch (Exception e) { // 解析失败,说明token无效或过期 } } response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write("{\"code\":401,\"msg\":\"未登录或登录已过期\"}"); return false; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { UserContext.clear(); } }注意,如果项目里引入了Redis,更常见的做法是登录成功时把Token存一份到Redis,并设置过期时间,拦截器每次校验JWT的同时再查一下Redis里有没有这个Token。这样可以在“用户修改密码”或“管理员封禁用户”时,主动删除Redis里的Token实现强制下线,这就是所谓的“JWT无状态方案的补偿手段”。
3.2 商品搜索与缓存设计
商品列表页通常在首页和分类页都会出现,属于典型的“读多写少”场景,非常适合做缓存。最简单有效的模式是Cache Aside模式:先读缓存,缓存没有则查数据库,然后把结果写入缓存,并设置过期时间。
我在做模拟项目X的时候,对首页商品分类列表的缓存是这样设计的:key是category:list:parentId,value是JSON数组,过期时间设置为30分钟。商品详情页的缓存key是product:detail:{id},过期时间设置为60分钟。要注意缓存穿透问题,如果有人恶意请求一个不存在的商品id,每次都绕过缓存打数据库。解决办法是:缓存空值,或者用布隆过滤器做前置判断。毕业设计阶段采用缓存空值比较简单,给不存在的id在Redis里也存一个空对象,过期时间很短(比如3分钟),就能挡住大部分恶意流量。
搜索功能如果只做MySQL的like查询,接口大概是这样的:
public PageResult<ProductVO> search(String keyword, int page, int size) { LambdaQueryWrapper<Product> wrapper = new LambdaQueryWrapper<>(); wrapper.like(Product::getName, keyword) .or().like(Product::getSubTitle, keyword) .eq(Product::getStatus, 1); Page<Product> p = new Page<>(page, size); productMapper.selectPage(p, wrapper); // 转VO返回 }这段代码看起来简单,但有几个坑。第一个坑:keyword为空时,like('%%')会全表扫描,所以要做空值判断,keyword为空就返回空列表或热门商品。第二个坑:用OR连接条件时,要注意和后面的status条件组合,最好用and嵌套,否则SQL逻辑容易出错。第三个坑:如果商品名称是中文,MySQL的like在utf8mb4下没问题,但搜索效率很低,因为是全表扫描,不过对于毕业设计的演示数据量来说完全够用。
3.3 下单减库存的并发处理
这是整个项目里技术含量最高的点,也是你论文加分的核心章节。下单的常规流程是:前端提交购物车勾选的商品列表,后端校验商品是否在售、库存是否足够,然后计算总价,生成订单号,扣减库存,插入订单主表和明细表,最后清空已下单的购物车记录。
但问题来了:如果是并发请求,比如两个人同时抢购最后一个商品,代码里“先查询库存,再扣减库存”的两个步骤之间存在时间差,就会双双判断库存大于0,最终导致库存变成负数。
解决方案有几档,由简到难:
第一档,在扣减库存的SQL语句上加条件,让数据库帮我们守住底线:
update product set stock = stock - 1 where id = #{productId} and stock > 0这样即使并发请求同时执行,数据库的行锁也只会让一条更新成功,另一条影响行数为0,代码里判断受影响行数即可。
第二档,用乐观锁,给商品表加一个version字段:
update product set stock = stock - 1, version = version + 1 where id = #{productId} and version = #{version}更新失败说明version已变,重新查库存再重试。乐观锁在读多写少的场景下性能较好,但如果冲突多,重试成本也不小。
第三档,用Redis原子操作+Lua脚本扣减库存:
String script = "if redis.call('get', KEYS[1]) >= ARGV[1] then " + "redis.call('decrby', KEYS[1], ARGV[1]) return 1 else return 0 end"; Long result = redisTemplate.execute( new DefaultRedisScript<>(script, Long.class), Arrays.asList("product:stock:" + productId), String.valueOf(quantity) );这种方式扣减库存的耗时极短,抗高并发能力最强,但需要考虑数据库与Redis库存数据一致的最终一致性问题,处理起来更复杂。对于答辩来说,能够清晰讲出第一档或第二档方案的原理,并在代码里正确实现,已经能超过大部分人了。
还有一个容易被忽略的点:订单号生成。用自增id做订单号肯定不行,会被猜到订单量。常规做法是时间戳 + 随机数,或者用Snowflake算法。Snowflake的核心是64位long型,前41位是毫秒时间戳,中间10位是机器id,最后12位是序列号,同一毫秒内可以生成4096个不重复id。实际项目里直接用MyBatis-Plus的IdWorker.getId()方法就行。
3.4 支付结果回调与订单状态流转
毕业设计里的支付功能,绝大多数是“模拟支付”,因为接入真实支付渠道需要商户资质,而且很麻烦。模拟支付的做法是:前端在“待支付订单”页面点击“模拟支付”按钮,后端直接把订单状态从0(待支付)改为1(已支付),记录支付时间。
但如果你想做得更专业一些,可以设计一个“支付回调接口”,模拟真实支付系统中“支付平台主动通知商户系统支付结果”的过程。这个接口放在订单模块里,接收一个简单的请求体,比如:
@PostMapping("/api/pay/callback") public Result payCallback(@RequestBody PayCallbackRequest request) { String orderNo = request.getOrderNo(); // 1. 根据订单号查订单,判断当前状态是否为待支付 // 2. 流水号校验,防止重复通知 // 3. 更改订单状态为已支付 // 4. 记录支付时间 // 5. 异步通知用户(模拟) }这个设计的妙处在于,你可以把“支付回调幂等性”讲清楚:如果支付平台因为网络原因重复推送回调,你的接口必须保证订单状态只从待支付变成已支付一次,后面重复的通知直接忽略。实现方法很简单:更新订单时带上status=0条件,影响行数为0说明已经处理过了。
订单状态流转的核心代码,本质上就是按条件更新:
// 支付 boolean paid = orderMapper.updateStatusByOrderNo(orderNo, 0, 1) > 0; // 发货 boolean delivered = orderMapper.updateStatusByOrderNo(orderNo, 1, 2) > 0; // 完成 boolean finished = orderMapper.updateStatusByOrderNo(orderNo, 2, 3) > 0;这里最好用一条带条件的update SQL,不要把查和改成两步,否则又有并发问题。
4. 从源码到论文:文档化能力才是答辩里的隐藏分数
4.1 论文结构怎么排才像“设计实现”而不像“代码说明书”
很多同学有一个误区:以为论文就是把源码里的类和方法抄一遍。评委最反感的就是这种“代码说明书”。合格的毕业设计论文,通篇应该围绕“需求分析-技术选型-系统设计-系统实现-系统测试”这条主线展开,讲清楚每一个设计决策的理由。
常规的论文大纲大概是:第一章绪论(背景、意义、国内外研究现状);第二章相关技术介绍(Spring Boot、MyBatis-Plus、Redis、Vue);第三章系统分析(可行性分析、功能需求分析、非功能需求分析);第四章系统设计(总体架构设计、功能模块设计、数据库设计、接口设计);第五章系统实现(分模块贴核心代码截图并说明逻辑);第六章系统测试(功能测试用例表格、接口测试结果、性能测试简述);第七章总结与展望。
这里有一个技巧:第四章的数据库设计部分,一定要画出完整的ER图和表结构说明表。ER图用Visio或者draw.io画就行,表结构说明用三线表展示字段名、类型、约束、说明。光是这些表格,就能占掉论文很大篇幅,而且看起来非常“规范”。
第五章系统实现不要每个功能都贴代码,而是挑三到四个有亮点的模块贴核心代码片段,比如JWT登录拦截器、Redis购物车、带乐观锁的下单逻辑。每个片段配上一段“实现思路”和“运行效果截图”,这就非常充实了。
4.2 图表怎么画:ER图、流程图、时序图
论文里不能只有文字,图表是最直接的设计体现。ER图建议只画核心实体及关系:用户、分类、商品、购物车、订单、订单明细、管理员。实体之间关系:一个用户有多个订单,一个订单包含多个订单明细,一个商品属于一个分类。
流程图重点画两处:一是用户下单流程,从点击提交订单到扣库存、生成订单、支付成功、状态更新;二是管理员处理订单流程,从查看订单到发货、完成。流程图用Visio或ProcessOn画,线上也有免费的draw.io,导出图片插入Word,注意统一图片风格。
时序图强烈建议画一个“用户登录”的时序图,展示前端、Controller、Service、Mapper、数据库之间的调用关系。这张图能非常直观地体现你对Spring Boot分层架构的理解,评委看到会点头的。
4.3 答辩PPT的演示节奏与提问预演
答辩PPT一般10到15分钟,千万不要照着论文念。我的建议是PPT总页数控制在12到15页,节奏大概是:第1页封面含题目、姓名、指导教师;第2页项目背景与目标;第3页技术选型及理由;第4页系统功能结构图;第5页系统架构图;第6页数据库ER图;第7页核心代码展示(登录+下单二选一);第8到10页系统运行效果截图(用户端、管理端各放两三张);第11页系统测试结果;第12页总结与展望。
演示的时候,实际操作环节最好用“核心链路演示法”,也就是只演示最关键的几条路径:注册登录 -> 商品搜索 -> 加入购物车 -> 结算下单 -> 模拟支付 -> 查看订单 -> 管理员发货。其他的旁支功能可以口头带过,避免演示中途出bug。
答辩提问环节,评委大概率会问这几个问题:你的系统架构是什么?为什么选Spring Boot?购物车为什么用/不用Redis?订单状态怎么管理的?数据库表怎么设计的?项目有什么不足?这些问题的“标准答案”,其实在论文里都已经有了,你要做到的是脱稿说清楚。我还有个小技巧:答辩前自己在电脑上完整走一遍主流程,把常见报错和应对方法写在便利贴上,关键时刻能救场。
5. 常见问题与排查实录:我也是踩坑踩过来的
5.1 运行环境与依赖冲突问题
这个项目最常见的启动失败原因,第一是JDK版本不对。Spring Boot 2.x建议用JDK 8或11,Spring Boot 3.x要求JDK 17及以上。如果你下载的源码是Spring Boot 2.x却在JDK 17上跑,很可能出现IllegalStateException或者CGLIB相关报错。建议创建项目前先看清楚pom.xml里parent版本,配好一致的JDK。
第二是Maven依赖下载慢或失败。国内网络环境下,建议在Maven的settings.xml里配置阿里云公共仓库镜像。依赖下载失败的表现是编译时一堆“Cannot resolve symbol”,解决方案是clean再reimport,还不行就删掉本地仓库对应文件夹重新下。
第三是端口冲突。Spring Boot默认8080,如果本机装了其他服务占用8080,启动会报Port already in use。要么改application.yml里的server.port,要么找到占用进程干掉。Windows下可以用netstat -ano | findstr 8080然后taskkill /PID xxx /F。
5.2 数据库中文乱码与时间字段问题
中文乱码是另一个高频问题。首先要确保MySQL数据库和数据表的字符集是utf8mb4,创建数据库时用CREATE DATABASE xxx DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci。然后检查Spring Boot连接串,一定加上characterEncoding=utf8,例如:
url: jdbc:mysql://localhost:3306/mall?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai不配置serverTimezone的话,可能会出现时间比当前时间早8小时的问题,因为你本地是东八区,而JDBC驱动默认用了UTC。还有一个小坑:Java实体类里的LocalDateTime和MySQL的datetime类型映射没有问题,但如果用String去接datetime,格式化就会很丑。建议统一用LocalDateTime,并在JSON输出时加@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")注解。
5.3 并发下单数据不一致的排查
我在做压力测试时发现过一个问题:用JMeter并发100个线程抢同一个商品,后台日志里显示库存扣到了负数。这个问题的根源就是前面说的“先查后改”没有加并发控制。解决后我再测,发现库存正确归零,但偶尔会出现“下单成功但订单明细商品名称为空”的情况。查了半天,发现原因是商品信息从库里查出后,在转换成订单明细对象时,某个字段的setter没调用。这种问题很难通过看代码发现,我是通过在订单明细插入前打印日志定位的。
所以我的建议是:下单接口里,关键节点一定要加日志,生产环境可以关掉,但开发调试时必须留着。另外,测试时多用真实数据,不要只测一个商品id,用多个商品混着下单,更容易暴露问题。
5.4 外链资源与前端联调问题
很多下载来的项目资料里,前端页面会引用一些外部的图标库、字体库或图片CDN地址。演示环境没有外网时,整个页面会变得非常难看,图标全变成小方块。这种问题的处理方式是:提前把用到的静态资源下载到本地,放到项目的static目录下,或者使用本地依赖的npm包,不要依赖远程公共CDN。
前后端联调时还有一个常见的坑:跨域。前端跑在localhost:8081,后端跑在localhost:8080,前端请求后端接口会被浏览器拦截。解决方案是写一个CORS配置类:
@Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.addAllowedOriginPattern("*"); config.addAllowedMethod("*"); config.addAllowedHeader("*"); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } }注意,如果后端开启了自定义拦截器,跨域预检请求OPTIONS也会进入拦截器,所以要像前面登录拦截器代码里那样,对OPTIONS请求直接放行,否则前端会一直提示跨域错误。
在这个项目上,我确实踩过不少坑,但回过头来看,踩坑本身就是收获。下载源码只是第一步,把项目跑起来、把每一行核心代码弄明白、把论文和PPT组织成自己讲得清楚的故事,这个过程才是真正长本事的地方。
如果让我给一个最务实的建议,那就是:拿到任何项目资料,先花一天时间把环境跑通,再花两天时间走读核心模块代码,最后用一天时间把“登录-下单-支付-管理”这条主链路完整地讲给同学听。能做到这一步,不管评委问什么,你都能接得住。
最后再分享一个小技巧:答辩PPT上不要写太多文字,每一页只保留“功能名称 + 截图 + 关键技术点”,把设计思路和亮点放进你的口头讲述里。评委看的是你对项目的掌控力,而不是PPT本身。预祝顺利。