ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SpringBoot+Vue全栈电商系统实战:从环境搭建到Redis分布式锁与缓存优化

SpringBoot+Vue全栈电商系统实战:从环境搭建到Redis分布式锁与缓存优化 前两天有人问我现在想找一个能完整跑通前后端、又能写在简历上的Java项目到底该选什么。我直接把这个SpringBootVue全栈电商系统的源码包甩了过去——不是因为它花里胡哨而是因为它把Java在线商城项目该有的东西全都占了SpringBoot做后端、Vue做前端、MySQL存业务数据、Redis扛缓存和分布式锁Maven管构建还附带万字详细文档。对于想系统过一遍全栈开发流程、或者准备毕业设计的人来说这个项目的学习曲线和覆盖面都卡得刚刚好。这套源码我已经完整跑过一遍也带着几个人从零搭起来过。它不是那种只给一堆散装代码让你自己拼的仓库而是真能启动、真能下单、真能把订单状态和库存扣减串起来的那种完整度。这篇东西就把我从拿到源码到跑通、再到二次开发过程中实际遇到的问题和调试思路整理出来给准备啃这个项目的人一条相对顺手的路。1. 项目整体设计与模块拆分思路1.1 为什么SpringBootVue是这类项目的稳妥选择做Java在线商城项目技术栈的选型其实没什么悬念。SpringBoot现在基本是Java后端的事实标准它的自动配置机制让项目不用再像SSH时代那样写一堆XML配置文件一个spring-boot-starter-web就能把内嵌Tomcat、Spring MVC、Jackson序列化这些基础能力全带进来。Vue在前端这边的地位也类似组件化开发配合Vue Router做页面跳转、Vuex或Pinia做状态管理前后端通过JSON交互写起来效率确实高。这个项目的一个优势在于它的技术选型不是什么新上什么而是什么稳用什么。MySQL 5.7或8.0都可以跑Redis用3.x以上版本就行Maven管理依赖也不挑版本。对新手来说这意味着你不用在版本兼容性上消耗太多精力。我之前见过有人选了个刚出的JDK 21配合最新SpringBoot 3.x结果一堆第三方库不兼容光调环境就花了一周。这个项目老老实实用SpringBoot 2.x JDK 8/11踩坑概率小得多。更关键的是这个项目的模块拆分方式是照着真实电商系统的骨架来的商品模块、用户模块、购物车模块、订单模块、支付模块每个模块的边界清楚调用关系也不复杂。这种拆法不是随便分的它遵循的是高内聚低耦合的思路——商品服务和订单服务之间不直接操作对方的表而是通过Service层的方法调用或者DTO传输数据。你照着这个结构去读代码逻辑会非常顺。1.2 核心需求拆解一个电商项目到底要解决哪些问题说白了电商系统的核心就三件事商品展示、购物决策、交易履约。商品展示需要一个商品分类体系和商品详情页购物决策依赖购物车和库存实时性交易履约则涉及订单生成、库存扣减、支付回调、状态流转这些环节。这个项目对这些需求的实现方式是层层递进的。商品模块做了分类表product_category和商品表product_info的两级结构支撑首页分类导航和搜索筛选购物车模块用Redis来临时保存用户未登录状态下的购物车数据用户登录后合并到MySQL里的cart_item表订单模块是重头戏涉及订单主表order_master和订单明细表order_item通过事务保证订单插入库存扣减要么都成功、要么都失败。Redis在这里的定位很值得留意。它不只是用来做缓存还承担了分布式锁的职责。在订单创建流程中需要扣减库存如果直接操作MySQL里的库存字段在高并发下会出现超卖问题。项目用Redis的SETNX命令实现了一个简单的分布式锁保证同一时间只有一个线程能执行扣减操作。虽然它的实现比Redisson的看门狗机制要原始一些但对于学习而言反而更容易理解分布式锁的本质逻辑。1.3 从源码结构看懂项目学习路径拿到源码之后第一步不是急着启动而是先把目录结构过一遍。这个项目的代码组织方式很标准├── backend │ ├── src/main/java │ │ └── com/example/mall │ │ ├── controller │ │ ├── service │ │ ├── dao │ │ ├── entity │ │ ├── config │ │ └── common │ ├── src/main/resources │ │ ├── application.yml │ │ └── mapper ├── frontend │ ├── src │ │ ├── api │ │ ├── router │ │ ├── views │ │ ├── components │ │ └── store └── doc └── 项目文档.md阅读顺序我建议是先看数据库设计文档 → 再读Controller层 → 然后钻进Service层 → 最后看前端页面怎么调用接口。数据库设计是理解全项目的钥匙它告诉你系统的数据模型长什么样Controller层展示了系统的接口列表和参数结构Service层则包含业务的真正逻辑比如订单号生成、库存操作、状态机流转等。前端部分留在后面看因为你需要先知道后端提供了什么能力才能理解页面为什么那样写。这个项目带了万字文档这一点在源码项目中其实挺难得的。文档不是复制粘贴的README而是从环境搭建到表结构说明、再到核心接口的调用方式都有涉及。我的建议是第一遍跟着文档跑通第二遍丢掉文档自己读代码遇到看不懂的地方再回文档查这样印象最深。2. SpringBoot后端核心实现细节2.1 分层架构与各层的职责边界这个项目的后端采用了经典的三层架构Controller层接收并校验请求参数Service层处理业务逻辑DAO层Mapper负责数据库读写。你读代码时经常会看到Controller里的方法体非常薄多数只有几行——创建一个参数对象调用Service方法然后封装返回结果。这个设计是对的Controller本身不应该承载业务逻辑它的职责只是协议的适配层和参数的简单校验。Service层是这个项目的精华所在。比如订单模块的OrderServiceImpl它会协调用户地址、购物车数据、商品信息、库存余额、订单号生成器等多个组件。这里要注意观察事务注解Transactional是怎么用的——不是每个方法都加事务而是只有涉及多表写操作的方法才加。如果读代码时发现某个Service方法既修改了订单表又扣减了库存但它没有事务注解那就是一个潜在的坑后面我会讲到这类问题的排查方式。DAO层用的是MyBatisSQL写在XML文件里。为什么不选择Spring Data JPA从项目选型角度猜测应该是考虑到电商场景下SQL的可控性和优化空间。MyBatis的XML让你能精确控制每一条SQL的执行计划在复杂联表查询、动态条件拼装、批量操作时更灵活。这一点和Hibernate那种自动生成SQL的思路是两条路线前者更适合SQL优化需求强的业务系统。2.2 配置文件的密码、连接池与序列化配置application.yml是启动项目前必须仔细过一遍的文件。除了数据源地址、Redis地址这些基础项之外有几个配置项值得单独拎出来说明。数据源层面项目用的是Druid连接池配置了initialSize、minIdle、maxActive这几个核心参数。连接池不是越大越好它的大小应该跟数据库实例的CPU核心数和连接处理能力匹配一般maxActive设为20到50之间就比较合理了。如果你发现系统在压测时出现连接获取超时优先考虑的应该是SQL执行慢导致连接被长时间占用而不是盲目调大连接数。Redis的配置里有一个点容易忽略lettuce和jedis是两种不同的客户端实现。这个项目如果用的是Spring Boot默认的lettuce它在高并发下的表现比jedis更稳定因为lettuce基于Netty实现多路复用一个连接可以并发处理多个请求。但lettuce在Redis主从切换场景下的自动重连机制表现得不够智能如果做生产部署可以在这个问题上做针对性的测试。序列化方面项目一般会配置Jackson的LocalDateTime格式支持。JDK 8的时间类型如果没配序列化规则接口返回的日期会变成一串数字前端没法直接展示。解决方式是加一个JacksonConfig配置类注册JavaTimeModule并指定日期格式我用这种方案处理时接口返回的时间就是yyyy-MM-dd HH:mm:ss格式前端拿到后可以直接渲染。2.3 登录鉴权Token方案还是Session方案这个项目的登录鉴权实现方式从实用角度来说是一个很好的学习范本。它没有引入Spring Security全家桶那套东西对新手来说学习成本太高而是自己实现了一个基于Token的鉴权机制用户登录成功后后端生成一个Token通常是把用户ID和过期时间加密后得到的字符串存到Redis中并设置过期时间前端把Token存到localStorage中每次请求时在请求头里带上后端通过拦截器HandlerInterceptor校验Token是否有效顺便把用户信息塞到ThreadLocal里方便后续业务代码直接获取当前用户。这个方案的优点在项目里很直观无状态、易扩展、贴合现在前后端分离的场景。缺点也是有的——Token存Redis之后每个请求都要查一次Redis会多一次网络开销不过这个损耗对中小型系统来说完全可以接受。如果你打算拿这个项目做二次开发我建议不要一上来就替换成Spring Security JWT。先理解这套简单方案的实现逻辑Token生成、存储、校验三个环节然后在它的基础上增强安全性细节比如增加请求签名校验、Token定期刷新机制、设备信息绑定等。安全是一个循序渐进的问题一上来追求大而全反而容易出漏洞。3. Vue前端与后端联调的落地实践3.1 前端项目结构和Vue Router配置要点前端部分用的是Vue脚手架工具生成的工程核心目录是src/views、src/components、src/api、src/router。views里放的是页面组件一个路由对应一个页面components里放的是可复用的UI组件比如商品卡片、分页器、数量选择器等api目录下封装了所有与后端交互的接口调用router负责维护URL与页面的映射关系。Vue Router在配置时有一个容易被新手忽略的点路由懒加载。项目里如果用的是component: () import(/views/xxx)这种写法那么Webpack会把每个页面单独打包成一个chunk用户访问某个页面时才加载对应的JS文件。我测过如果不用懒加载首屏要把所有页面的代码全下载下来体积会比懒加载大很多白屏时间明显变长。所以如果你想优化首屏加载速度先看看路由有没有做懒加载这一步比纠结压缩插件更有效。3.2 Axios封装与请求拦截器的关键作用前后端交互的核心工具是axios但直接在组件里到处this.$http.get这种写法是撑不住的。项目里应该会有一个封装好的axios实例统一设置了baseURL、超时时间、请求拦截器和响应拦截器。请求拦截器主要做三件事从localStorage取Token并放到请求头判断请求是否需要进行防重复提交处理把参数处理成后端期望的格式。响应拦截器则负责统一处理后端返回的code/data/message结构判断接口是否成功失败时弹错误提示。这个封装的价值在于你把鉴权、错误处理、Loading控制全部收敛到一个文件里后续新增页面只需要调用封装好的方法不用重复处理Token和异常。我在跑这个项目时本来觉得拦截器没什么值得琢磨的直到有一天发现登录状态明明过期了页面上的数据请求却还在一直发。排查后发现是响应拦截器里没有对401状态码做统一跳转处理。加了这么一段逻辑才好一旦响应码是401就清空本地Token跳转登录页。这是很典型的线上问题建议你拿到项目后立刻检查这块是否完备。3.3 前后端联调的CORS与本地代理配置前后端分离开发中最烦人的问题之一就是跨域。Vue开发服务器默认跑在localhost:8080后端接口在localhost:8088浏览器同源策略会直接拦截跨域请求。项目里解决这个问题的方式是在vue.config.js中配置devServer的proxy代理把/api前缀的请求转发到后端地址。这个方案的原理其实很简单浏览器访问前端服务器的/api/getUserInfo开发服务器接收到请求后因为配置了代理规则会自己作为中转把请求发到http://localhost:8088/api/getUserInfo拿到响应后再返回给浏览器。这样一来在浏览器看来请求是同源的规避了跨域问题。这个方案比后端开启CORS更推荐原因是它只在开发环境下生效生产环境部署时由Nginx统一处理反向代理你不需要为了本地开发而在后端代码里放宽跨域限制。我在联调时遇到的一个常见问题是代理配置写法错误会一直报404pathRewrite写错导致/api前缀没有去掉后端接口实际是/user/info而不是/api/user/info请求就打到错误地址上了。遇到这种问题先把代理配置里的pathRewrite值打印出来对照一下。4. Redis缓存与分布式锁的实战解析4.1 缓存穿透、击穿与雪崩的应对策略这个项目里Redis的使用很有层次感商品分类信息用缓存商品详情用缓存购物车在未登录状态下也放在Redis里。但有缓存的地方就有缓存和数据库一致性的问题也有缓存异常带来的稳定性问题。第一个要处理的是缓存穿透查询一个根本不存在的商品ID时Redis里没有MySQL里也没有请求直接打到数据库恶意攻击时可以顺着这个漏洞把数据库打崩。项目的应对方法是把空值也缓存起来设置较短的过期时间比如5分钟。这样同一个不存在的ID再来查询时Redis直接返回空不会打穿到数据库。这种方案叫缓存空值简单有效唯一要注意的是空值的过期时间别太长否则数据后来真新增了用户还是查不到。第二个是缓存击穿某个热点商品的缓存刚好过期同一时刻大量并发请求涌进来全部绕过Redis打到MySQL。应对方法是加互斥锁只有一个线程去数据库查询并重建缓存其他线程等待或直接返回旧值。项目的实现里这一块通常用Redis的SETNX做一个简单的互斥锁虽然不如Redisson那么完善但逻辑是通的。第三个是缓存雪崩大量缓存同时过期导致请求全量涌入数据库。应对方式是在设置缓存过期时间时加入随机数避免同一时刻大面积失效。这个细节项目文档里不一定写了但你在读代码时如果看到过期时间是一个固定值建议改成基础过期时间 随机偏移量的模式。4.2 分布式锁在库存扣减场景的代码走读库存扣减是分布式锁最典型的使用场景。我直接把这个项目里库存扣减的核心逻辑拆出来讲因为它值得逐行读。正常流程是这样的前端提交订单后端收到请求后先去Redis里尝试获取锁锁的key通常是lock:stock:商品IDvalue是一个唯一标识UUID或者时间戳加用户ID。如果获取成功就执行查库存 → 判断库存是否足够 → 扣减库存 → 创建订单这一串操作最后释放锁。如果获取失败说明有其他线程正在处理同一商品的库存这个请求要么等待重试要么直接返回正在处理的提示。这里有一个非常经典的实操陷阱锁释放时机不对。有些代码会把锁写在业务方法外面比如在Controller里加锁Service里做扣减方法执行完后在finally里释放锁。这个做法的问题是如果业务执行时间超过了锁的过期时间锁自动失效了另一个线程拿到锁后也开始扣减库存前一个线程执行完释放锁的时候释放的可能已经是别人持有的锁了。解决办法有两个一是用Lua脚本把判断是不是自己持有的锁 删除锁做成原子操作二是用Redisson那样的看门狗机制自动续期。项目里如果只是用SETNX 手动DEL你要意识到这是教学版实现线上必须加版本号或唯一标识检查。4.3 Redis在购物车与Session场景的替代价值购物车是电商里对Redis依赖最深的一个模块。未登录状态下用户往购物车里加商品数据存哪里用Express或传统Session方案也能实现但Session数据在服务端是内存存储进程重启就丢了存在Redis里则天然支持持久化和集群共享。这个项目把未登录购物车直接放在Redis的Hash结构里key是用户设备的访问标识field是商品IDvalue是商品数量这样设计的好处是用户加入购物车只需要一个HSET命令查询购物车也只需要一个HGETALL命令性能极佳。用户登录后项目需要把Redis里的临时购物车合并到MySQL中。合并时的冲突处理逻辑是MySQL里已存在同款商品就以两者数量之和为准并清掉Redis里的记录。这段代码虽然不长却是临时态转持久态的经典实现数据一致性视角值得细读。另外Redis里存的Token其实也是一种Session替代方案。想想看如果用户的登录态存在Tomcat的内存里重启机器后所有用户都要重新登录但存到Redis里重启后端服务完全不影响登录状态。这也是为什么现在的分布式系统普遍偏向缓存中间件 Token模式而不是传统Session。5. MySQL表结构设计与索引优化思路5.1 九张核心表的关系梳理这个项目的数据库设计文档值得先花半小时过一遍。核心表之间的关联关系我梳理下来大概是这样的user用户表存账号、密码加密后的密文、昵称、手机号product_category商品分类表parent_id自关联支持多级分类product_info商品表包含商品名称、描述、价格、库存、主图地址关联分类cart_item购物车表关联用户ID和商品ID记录数量与选中状态order_master订单主表存订单编号、总金额、订单状态、收货信息快照order_item订单明细表记录订单内每一个商品的快照包括下单时的价格address收货地址表关联用户ID存收货人、手机、详细地址payment支付流水表记录支付单号、支付金额、支付方式、回调状态product_comment商品评论表关联订单明细和用户存评论内容与评分这个结构有一个非常重要的设计理念订单明细里的商品价格是快照而不是实时关联商品表。也就是说下单时商品是什么价格订单明细里就存什么价格之后商品涨价降价都不会影响历史订单。这个细节在面试里经常被人拿出来问——如果关联商品表实时取价格你改一次商品价格历史订单的金额全部变了这是绝对不可接受的。项目里只要遵循下单快照原则这笔账就算得清。5.2 订单号生成方案与唯一索引约束订单号是电商系统里一个容易让新人翻车的设计点。数据库表的主键id是自增的但对外暴露的订单编号不能直接用自增ID原因有两个一是自增ID会暴露系统的订单量容易被竞对爬取二是自增ID不具备业务含义出了问题不好回溯。这个项目生成的订单号一般是日期前缀 时间戳 随机数的组合或者用用户ID后四位 时间戳 随机数的组合保证唯一性的同时也方便按日期维度去排查问题。除了业务订单号表上还有一些唯一索引值得注意比如user表的username字段、order_master表的order_no字段。唯一索引不是为了查询加速更多是为了兜底——在代码层面万一出现了重复数据数据库层面至少还能挡一道。我在排查实际项目时发现很多线上脏数据就是代码没校验 表没加唯一索引双重失守造成的。所以看这个项目时留意一下哪些表加了唯一索引能帮你理解数据库是最后一道防线这句话。5.3 慢查询排查定位SQL性能瓶颈的常用手段项目跑起来之后如果你发现某些页面响应特别慢第一反应不应该是加缓存而是先看SQL执行计划是不是合理。打开MySQL的慢查询日志在my.cnf里配置slow_query_log为ONlong_query_time设为1秒然后重现一次卡顿操作慢查询日志里就会记录下那条SQL和耗时。拿到慢SQL之后用EXPLAIN看执行计划重点看type列和key列。type从上到下性能依次变差const、eq_ref、ref、range、index、all。如果看到all全表扫描说明这条SQL没有用到索引检查WHERE条件里的字段有没有被索引覆盖。商品列表页的常见情况是按分类查询时只在category_id上建了一个普通索引但排序还用了create_time导致MySQL需要先把分组结果全部查出来再内存排序性能瓶颈就出现了。解决方法是建联合索引(category_id, create_time)让索引天然满足查询顺序。还有一个容易被忽略的点条件字段上如果做了函数运算索引会失效。比如WHERE DATE(create_time) 2024-01-01MySQL没法直接使用create_time上的索引因为它要先对字段做一次函数计算。改成WHERE create_time 2024-01-01 AND create_time 2024-01-02这种区间写法索引就能正常利用了。这种细节在这个项目的某些复杂查询里可能就藏着排查慢SQL时多看一眼条件写法就会发现。6. Maven构建、环境部署与踩坑排查记录6.1 Maven配置与依赖冲突处理Maven在这个项目里的作用是把整个后端工程的依赖统一管起来。拿到源码后第一件事是检查本地的Maven仓库是否配置了阿里云镜像否则从Maven中央仓库下载依赖的速度会让你怀疑人生。在settings.xml中配置阿里云镜像后依赖下载速度能提升一个数量级尤其那些几十MB的依赖包差别非常明显。依赖冲突是这个阶段最容易冒出来的问题。比如项目里同时引入了两个不同版本的Jackson或者某个依赖传递引入了旧版的Guava运行时报NoSuchMethodError或ClassNotFoundException基本都是依赖冲突引起的。排查方法是在报错信息里找到具体类的全限定名然后在IDEA终端执行mvn dependency:tree搜索这个类属于哪个jar包再看看是谁引入了多个版本。找到后在pom.xml里用exclusion排除掉旧版本即可。项目文档里如果提到JDK 8即可运行那就老老实实用JDK 8。如果用更高版本有可能会遇到SpringBoot 2.x内嵌Tomcat与高版本JDK的兼容警告虽然大多数情况不影响运行但没必要平白给自己增加变量。6.2 前后端分离部署与打包细节项目开发完之后的部署方式这里给一套我实际用过的方案后端打包成jar包前端构建后的静态文件交给Nginx托管。后端的构建命令是mvn clean package -DskipTests构建产物在target目录下是一个可直接运行的jar包。运行就是java -jar xxx.jar但要注意jar包默认读取的配置文件是application.yml线上环境最好用spring.config.location参数指定外部配置文件这样改数据库密码、Redis地址时不用重新打包。比如java -jar mall.jar --spring.config.location/opt/mall/application-prod.yml前端的构建命令是npm run build产物在dist目录下。dist里的HTML、JS、CSS拷贝到Nginx的html目录并配置一个反向代理规则把/api请求转发到后端服务。这里有一个很容易踩的坑如果前端用了Vue Router的History模式不是Hash模式刷新某个子路由页面时会报404。解决办法是在Nginx配置里加try_files $uri $uri/ /index.html;让所有路由请求都回退到index.html由前端路由接管。6.3 运行时常见问题的排查清单这里把我实际跑这个项目以及帮别人排查问题时遇到的高频问题整理成一张表直接照着查可能比你在网上搜一堆帖更高效。现象可能原因排查命令/步骤启动时报数据库连接失败MySQL未启动、连接地址错误telnet 127.0.0.1 3306检查URL中的库名是否存在启动时报Redis连接失败Redis未启动或密码不匹配redis-cli ping检查application.yml密码配置前端请求接口全部401Token未传递或已过期打开浏览器开发者工具Network看Request Header有无Authorization前端请求接口404proxy代理配置错误或Nginx转发路径不对检查vue.config.js的pathRewrite是否去掉了/api前缀商品列表接口很慢SQL未走索引或缓存失效EXPLAIN执行计划检查type是否为ALL下单并发时库存超卖分布式锁未生效或锁粒度不对检查锁是否在事务外层、释放逻辑是否完整页面能跑但样式错乱前端构建时静态资源路径不对检查publicPath是否设为相对路径./上传的文件无法访问静态资源映射未配置检查WebMvcConfigurer的资源映射路径表格里列出的这些问题每一个单独拎出来都能写一篇排查文章。但在这个项目里它们的共性原因是跑项目前的配置检查做得不够仔细。我建议拿到源码后先按文档把环境变量对一遍——数据库版本、Redis版本、JDK版本、Maven仓库地址这四个要素确认没问题再启动项目能避掉八成以上的启动故障。6.4 项目二次开发的三个建议方向当你把这个项目的代码基本吃透之后可以挑一句话往外扩展。我比较推荐三条路线第一条线是做支付模块的闭环。这个项目如果支付回调的逻辑是模拟的你可以接入支付宝沙箱环境让支付流程真正跑通。这个扩展的技术价值在于你会接触到异步回调的验签机制、幂等处理、订单状态与支付状态的关联更新。面试聊到这个比单纯背八股文有说服力得多。第二条线是给商品搜索加Elasticsearch。把MySQL里的商品数据同步到ES利用ES的倒排索引做搜索。这个扩展天然涉及数据同步全量增量、分词配置、搜索结果高亮等环节做一遍你对搜索引擎的理解会上一个台阶。第三条线是引入消息队列削峰。在下单高峰时如果直接同步调用库存服务容易打崩数据库你可以加上RabbitMQ或RocketMQ让下单请求先落到队列里消费者异步处理。这个扩展能逼你去思考最终一致性、消息重试、死信队列这些问题实战价值非常高。7. 这个项目我踩过的几个坑与心得把项目从头跑通一遍之后有些心得不吐不快。第一别嫌文档里写的SQL建表脚本啰嗦把那些脚本一条条执行完再看代码你的理解速度会快很多——代码里的字段名、类型、注释都是跟着表走的表结构不熟代码里全是天书。第二遇到接口报错先去看后端日志而不是琢磨前端传参对不对这不是态度问题是排查效率问题——后端日志会直接告诉你异常在哪一行、哪个字段为null、哪个SQL语法不对比前端一个个console.log快得多。还有一个感受是这个项目的代码风格虽然不算极致优雅但胜在学习友好。它不会像一些生产级项目那样引入大量的设计模式和抽象层让你连入口都找不到。它就是直白的三层架构、直白的Mapper调用、直白的请求流转这对学习者来说其实是巨大的优势——先把直白的写法吃透了再去看那些花哨的架构设计才有意义。如果非要提一点改进空间我觉得是项目里对异常处理的粒度可以更细。很多方法只做了try...catch后抛出一个通用异常但线上排查问题时具体的错误码和上下文信息很重要。做二次开发时我建议你定义一套业务异常码比如订单不存在、库存不足、商品已下架分别对应不同的提示和HTTP状态码这样前端能做的交互就丰富多了。经历了这些坑之后这个项目在我心里的定位已经很明确它适合做第一份全栈项目练习但绝不是写完就完事了——真正让它变成简历亮点的是你基于它的扩展和思考。
返回列表