最近陆续有几位做课程设计的同学拿同一类源码来问我:网上购物商城系统,SpringBoot后端、Vue前端、MySQL数据库,标题写得很诱人——"【可直接运行】"。结果呢,每个人遇到的问题也出奇一致:有的后端一启动就报数据库连接失败,有的前端npm run serve起来之后页面白屏,还有的管理员账号怎么都登不进去。说好的"可直接运行",变成了"折腾一天还没跑起来"。
这套系统的门道其实不在业务有多复杂,而在你要把它当成一个整体来看待。SpringBoot负责把后端服务拉起来,Vue负责在浏览器里渲染页面,MySQL负责存数据,三者只要有一处版本不对齐,"可直接运行"就会变成"需要排错半小时"。这篇文章我就按自己这些年实测跑通类似项目的经验来写,先把这套商城源码从外到内拆一遍,再给出一条最稳的启动路径,最后把所有高频问题集中列出来。无论你是要做课设、想入门SpringBoot和Vue的全栈开发,还是打算拿这套源码做二次开发,应该都能直接参考。
1. 直接可运行的购物商城源码,拆开看其实包含三件事
1.1 功能边界:这套商城到底完整在哪
很多人一听"网上购物商城系统"就以为是个大工程,其实拿到源码后你会发现,这个体量对新手刚刚好。它本质上是一个B2C网上商城,带后台信息管理功能,典型结构是"用户端 + 管理端"双端并存。
用户端负责前台交易链路,常见模块大概是这些:
| 模块 | 用户端功能 |
|---|---|
| 账号 | 注册、登录、退出、个人资料修改 |
| 商品 | 商品列表、商品详情、分类筛选、关键词搜索 |
| 购物车 | 加入购物车、修改数量、删除、勾选结算 |
| 订单 | 创建订单、订单列表、取消订单、查看状态 |
管理端则负责"信息管理"这半边,商品和订单都是后台管:
| 模块 | 管理端功能 |
|---|---|
| 商品管理 | 商品新增、编辑、上下架、库存调整 |
| 分类管理 | 一级/二级分类维护 |
| 订单管理 | 订单查询、发货、状态变更 |
| 用户管理 | 用户列表、启用禁用 |
你会发现,这套源码并不是只做一个展示型页面,而是把"用户下单 -> 数据落库 -> 后台处理订单 -> 前端看到状态变化"整条闭环串起来了。这也是它能被叫做"信息管理系统"的原因:每个业务对象都有完整的增删改查和状态流转,而不是一个静态商城外壳。
1.2 技术栈分工:三大件各管哪一段
技术栈很经典,就是主流的前后端分离组合:
- SpringBoot负责后端接口层。工程内部分层通常很清晰:Controller接收前端请求,Service处理业务逻辑,Mapper操作数据库。SpringBoot内嵌了Tomcat,最终打成一个Jar包,
java -jar一跑就是一个完整后端服务。 - Vue负责前端界面层。使用Vue Router做页面路由,Axios请求后端接口,组件化的方式组织页面。开发环境里靠Node服务做热更新,改完代码浏览器自动刷新。
- MySQL负责数据存储层。用户、分类、商品、购物车、订单、明细这些数据都落在表里,后端Mapper通过SQL或MyBatis映射完成读写。
为什么要选这个组合?从学习角度看,SpringBoot封掉了大量Spring繁琐的XML配置,能让新手快速看到"接口->业务->数据库"这条主线;Vue前后端分离的模式又是现在企业开发的主流形态;MySQL则直观好懂,建几张表就能把业务结构看得明明白白。所以这套组合本身就适合做课设,也适合用来入门前后端分离开发。
1.3 "可直接运行"不等于"什么都不用配"
这是我最想强调的一点。源码标题写"可直接运行",意思是三样东西齐全:后端源码、前端源码、数据库初始化SQL脚本。它不是说双击什么文件就能跑,而是说——只要把环境对齐,就能按固定步骤跑起来。
所以拿到代码后,第一步不是急着启动,而是先找三个文件:
- 后端配置文件:
application.yml,里面是数据库连接和端口 - 前端依赖文件:
package.json,里面是依赖列表和启动脚本 - 数据库脚本:通常是
init.sql或mall.sql,里面是建库建表语句和初始数据
把这三个文件看明白了,这个项目在你手里就已经成功了一半。剩下的问题,几乎全是环境版本问题。
2. 环境版本先对齐:JDK、Node、MySQL一个都不能差
2.1 先看pom.xml和package.json,程序自己会告诉你要什么版本
很多同学上来就装最新版JDK、最新版Node,然后项目跑不起来,反过来怀疑源码有Bug。其实源码没毛病,是版本不对。
判断版本基线只需要看两个文件:
先开后端的pom.xml,看spring-boot-starter-parent版本号:
- 如果是
2.x系列,比如2.5、2.7,JDK用1.8或11就够了 - 如果是
3.x系列,JDK必须用17及以上,否则编译直接报错
再看前端的package.json,看vue和vue-cli的版本:
- Vue 2 + vue-cli项目,Node用14或16最稳
- Vue 3 + Vite项目,Node用16或18都可以
这两个文件就是项目的"说明书",先看它们再装环境,能避开80%的启动问题。
2.2 环境安装顺序与版本矩阵
按我的习惯,装环境的顺序是固定的:JDK -> Maven -> Node -> MySQL。每个版本建议如下表:
| 组件 | 推荐版本 | 注意事项 |
|---|---|---|
| JDK | 1.8 / 11(Spring Boot 2.x);17(Spring Boot 3.x) | 配好JAVA_HOME,命令行java -version能通 |
| Maven | 3.6及以上 | IDEA自带Maven也行,重点是能拉依赖 |
| Node | 14 / 16(Vue 2);16 / 18(Vue 3) | 建议用nvm做多版本切换,别一棵树上吊死 |
| MySQL | 5.7 / 8.0 | 记住root密码,后面要写进配置文件 |
这里单独把Node拎出来强调一下:Vue 2老项目对Node版本非常敏感,Node 17以上跑webpack经常报error:0308010C:digital envelope routines::unsupported,一句话就是新版Node的OpenSSL和旧webpack不兼容。你当然可以给启动命令加参数绕过,但最简单粗暴的办法就是切换Node 16。这也是为什么我强烈建议用nvm管Node版本,一个命令就能换版本,比卸载重装舒服太多。
2.3 初始化数据库的完整操作
数据库是整套系统的基础,这一关过不去,后端怎么启动都会挂在数据库连接上。
先用命令行或图形化客户端连上MySQL,然后执行:
create database mall default character set utf8mb4 collate utf8mb4_general_ci; use mall; source /path/to/mall.sql;注意字符集一定要用utf8mb4,别用默认的utf8。utf8mb4是完整的UTF-8支持,能存表情符号和生僻字,而且和绝大多数项目的字符集设定一致,能少掉一堆中文乱码问题。
导入之后,执行show tables;,如果能看到user、category、product、cart、orders这类表,说明脚本导入成功。有些SQL脚本里已经自带create database语句,那你直接在命令行执行source整个脚本就行,不需要手动建库。
还有一个小坑:如果脚本里有初始管理员账号,记得看一眼初始密码,登录后台要用。很多项目默认管理员是admin,密码是123456或者admin123,具体看脚本里的INSERT语句。
3. 后端启动环节:数据库连接、拦截器白名单、跨域三件套
3.1 第一步永远是改application.yml
后端启动前,必须先把数据库连接配置改成你自己的。
打开application.yml,找到spring.datasource这一段:
server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/mall?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver这里有几个参数需要逐一说清楚:
jdbc:mysql://localhost:3306/mall里的mall必须和你建库时名字一致,不然报Unknown database。useUnicode=true&characterEncoding=utf8是为了中文不乱码,几乎每个商城项目都不能少。serverTimezone=Asia/Shanghai解决MySQL时区报错。旧版驱动不指定时区会直接启动失败。allowPublicKeyRetrieval=true是MySQL 8才需要的。如果你本机是MySQL 8而源码默认配置是给MySQL 5写的,不加这个会报Public Key Retrieval is not allowed。driver-class-name也要注意:MySQL 8用com.mysql.cj.jdbc.Driver,MySQL 5用com.mysql.jdbc.Driver,写错会提示找不到驱动类。
如果源码里还依赖了Redis,比如用Redis存token或做缓存,那启动前要把Redis也拉起来,否则后端启动时会反复尝试连接Redis,直到超时报错。这种情况在较完整的商城项目里很常见。
3.2 Maven依赖拉取与Jar包启动
配置改完,用IDEA打开后端工程,右下角会开始拉Maven依赖。这一步考验的是耐心和网络,依赖下载完之前不要急着启动。
如果依赖一直卡着不动,检查两件事:第一,IDEA里Maven配置对不对,用的本地仓库路径在哪;第二,Maven的中央仓库访问速度。下载慢时可以给Maven的settings.xml配置国内镜像源,这是常规操作,不属于什么特殊手段,放心配就行。
依赖就绪后,可以直接在IDEA里运行主启动类,也可以打包成Jar再运行。我个人的习惯是先打包验证,命令行执行:
mvn clean package -DskipTests打包成功后,在target目录下会生成一个mall-0.0.1-SNAPSHOT.jar之类的文件,然后:
java -jar target/mall-0.0.1-SNAPSHOT.jar看到日志里出现Started MallApplication in x.xxx seconds,说明后端已经起来了。这时候可以先在浏览器访问一下http://localhost:8080/api/user/login之类的接口,能返回JSON就说明基础链路是通的。
3.3 登录鉴权:白名单没配好,接口全401
商城项目基本都有登录鉴权。比较常见的做法是:登录成功生成一个Token返回给前端,前端后续请求在请求头里带上Token,后端通过拦截器校验Token是否有效。
拦截器逻辑大致长这样:
public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { return true; } String token = request.getHeader("Authorization"); if (token == null || !JwtUtil.verify(token)) { response.setStatus(401); return false; } return true; } }注册拦截器时,最关键的是放行公共接口:
registry.addInterceptor(new AuthInterceptor()) .addPathPatterns("/api/**") .excludePathPatterns( "/api/user/login", "/api/user/register", "/api/product/**", "/api/category/**" );白名单没配好,会出现一个非常经典的Bug:登录接口本身能通,登录成功之后前端再请求商品列表、购物车这些接口,全部返回401。排查思路很简单,先看请求头里到底有没有带上Token,再看后端拦截器是不是把公共接口也拦截了。
3.4 跨域配置:前后端分离的必经之路
前端和后端端口不同,比如前端8081、后端8080,浏览器就会因为同源策略拦截跨端口请求,控制台里报CORS错误。解决方式有两个:前端配代理,或者后端开跨域。开发环境通常用前端代理更简单,但如果你直接通过IP访问后端地址,或者想省事一点,后端直接加一个跨域配置也行。
@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); } }注意一个小坑:setAllowCredentials(true)的时候,addAllowedOrigin不能用*,要用addAllowedOriginPattern("*"),否则浏览器还是会拦。这个细节很容易漏,报错的时候又很不直观,建议直接照上面的写法来。
4. 前端Vue启动与调试:npm install只是开始
4.1 package.json决定启动方式
前端工程一般是一个独立目录,先看package.json的scripts字段:
"scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build" }Vue 2 + vue-cli项目启动命令是npm run serve;Vue 3 + Vite项目一般是npm run dev。别想当然,以实际package.json写的为准。
进入前端目录,依次执行:
npm install npm run servenpm install第一次跑通常会比较久,因为要装几百个依赖包。装完之后启动,看到Compiled successfully,然后打开浏览器访问http://localhost:端口,前端页面就出来了。
4.2 npm install最容易炸的两个点
第一是node-sass安装失败。node-sass是出了名的版本敏感,Node 17以上基本装不上。如果你在package.json里看到node-sass,最好的办法就是先把Node切到14或16,再重新npm install。如果项目里的样式工具是sass或dart-sass,那相对没用那么敏感。
第二是启动时报OpenSSL错误。如果你Node版本比较高,启动webpack时会看到:
error:0308010C:digital envelope routines::unsupported解决办法要么降Node版本,要么在启动命令里加环境变量。在package.json里可以这样改:
"scripts": { "serve": "NODE_OPTIONS=--openssl-legacy-provider vue-cli-service serve" }不过这样写Windows下可能有兼容问题,最好用cross-env包中转一下,或者干脆用nvm换到Node 16,省心太多。我自己遇到这种问题都是直接换Node版本,不加参数,因为参数只是掩盖了版本不匹配的根因。
4.3 devServer代理解决开发环境跨域
前端页面出来了,但页面上没有数据,打开浏览器控制台一看,全是CORS报错。这种情况十有八九是前端没配代理,或者代理指向错了。
在Vue 2工程里,配置写在根目录的vue.config.js:
module.exports = { devServer: { port: 8081, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } };这段配置的意思是:前端把以/api开头的请求,全部转发到http://localhost:8080,也就是后端地址。这样前端的请求经过代理转发,就不再触碰浏览器的同源限制。
这里有个很常见的坑:前端项目里封装请求时,baseURL可能写的是http://localhost:8080/api这样的绝对路径,而不是/api相对路径。如果baseURL是绝对路径,请求根本不会经过/api这个代理规则,CORS照样报错。正确做法是baseURL写/api,剩下的交给代理转发。
4.4 登录态保持与路由守卫
前端登录成功之后,需要把Token存起来,并在后续请求里带上。通常的做法是存在localStorage里,由Axios拦截器统一加请求头。
Axios请求拦截器里加上这一句:
service.interceptors.request.use(config => { const token = localStorage.getItem('token'); if (token) { config.headers['Authorization'] = token; } return config; });响应拦截器里处理401:
service.interceptors.response.use( response => response.data, error => { if (error.response && error.response.status === 401) { localStorage.removeItem('token'); router.push('/login'); } return Promise.reject(error); } );路由守卫则负责在页面跳转前判断登录状态:
router.beforeEach((to, from, next) => { const token = localStorage.getItem('token'); if (to.meta.requiresAuth && !token) { next('/login'); } else { next(); } });这套机制串联起来,用户未登录时访问购物车、订单这些页面,就会自动被踢回登录页,而不是看到了页面再调接口报401。
5. 数据库表结构与下单链路:价格快照和库存扣减才是重点
5.1 一套典型商城表结构长什么样
商场项目不管怎么写,核心表逃不出这几张:
| 表 | 作用 |
|---|---|
| user | 用户信息 |
| category | 商品分类 |
| product | 商品信息、价格、库存 |
| cart | 购物车记录 |
| orders | 订单主表 |
| order_item | 订单明细表 |
核心字段大概是这样的:
user:id、username、password、nickname、phone、avatar、role、create_timecategory:id、name、parent_id、sortproduct:id、category_id、name、subtitle、main_image、price、stock、sales、status、detailcart:id、user_id、product_id、quantity、checked、create_timeorders:id、order_no、user_id、total_price、status、receiver_name、receiver_phone、address、create_time、pay_time、ship_timeorder_item:id、order_id、product_id、product_name、product_image、current_unit_price、quantity、total_price
表之间的关联关系也很直观:user和orders是一对多,一个用户有多张订单;orders和order_item是一对多,一张订单有多条商品明细;cart里的商品只是"购物车快照",真正下单后数据会进入order_item。
5.2 为什么订单明细必须存商品快照
这一条是最值得新手记住的设计细节。
很多新手设计订单表的时候,只放一个product_id,以为查订单时去商品表里把名字和价格查出来就行。但这是错的。商品的价格、名称随时可能被后台修改,一旦你下单之后商家改了价格,历史订单再去商品表关联,看到的就不是下单时的价格了。
所以正规系统都会设计一张order_item明细表,下单的那一瞬间,把商品名称、主图、单价、数量全部复制一份存进去。这就像购物小票一样,小票上印的是你买的那一刻的单价,而不是"拿小票去系统里查现在的价格"。看字段名就能明白,current_unit_price这个字段,存的就是下单时刻的商品单价快照。
这个小设计,决定了这套源码是"能跑的教学Demo"还是"接近真实业务的信息管理系统"。
5.3 下单接口的完整步骤
下单接口是整套系统的核心,它的Service方法大概要干这么几件事:
@Transactional public OrderVO createOrder(Long userId, List<CartItem> selectedItems) { // 1. 查出购物车选中的商品信息 // 2. 逐个校验商品状态是否正常、库存是否足够 // 3. 用商品当前价格组装 order_item,重新计算总价 // 4. 扣减库存:update product set stock = stock - #{num} where id = #{id} and stock >= #{num} // 5. 生成订单号,保存订单主表 // 6. 删除已经下单的购物车记录 // 7. 返回订单号、实际应付金额 }这里面有两个关键点。
第一,总价必须在后端重新计算,不能直接信任前端传过来的价格。前端请求参数里带过来的只是商品数量和购物车明细ID,单价要以后端数据库查到的当前价格为准。否则用户把请求改一下,就能以超低价下单,这是最基础的安全底线。
第二,扣库存要防超卖。最简单的写法是:
UPDATE product SET stock = stock - #{num} WHERE id = #{id} AND stock >= #{num}这条SQL利用数据库的行锁和条件判断,保证即使多个用户同时下单同一件商品,也不会出现库存扣成负数。
方法上加了@Transactional注解,意思是整个方法是一个事务:扣库存和生成订单要么都成功,要么都回滚。绝对不能出现订单生成了、库存却没扣这种情况。
5.4 订单状态机怎么流转
订单状态一般用一个整型字段表示,常见约定:
| 状态值 | 含义 | 可执行操作 |
|---|---|---|
| 0 | 待付款 | 用户取消;超时自动关闭 |
| 1 | 已付款 | 商家发货 |
| 2 | 已发货 | 用户确认收货 |
| 3 | 已完成 | 无 |
| 4 | 已取消 | 无 |
用户端看到的是"待付款/已付款/已发货/已完成",后台管理端通过修改这个状态字段来驱动订单流转。比如商家点击发货,就是把status从1改成2;用户确认收货,就是把2改成3。
这个状态机很简单,但它是"信息管理系统"里非常典型的设计思路:数据在不同状态之间流转,每个状态都有对应的操作权限和页面呈现。看懂了这个,后面做物流、售后这些扩展功能都会有思路。
6. 跑通之后的改造路径:从"能运行"到"会改代码"
6.1 用"链路定位法"找到要改的文件
很多新手不敢改代码,是因为不知道往哪里改。这里分享一个我自己一直在用的"链路定位法":
- 打开浏览器开发者工具,切到Network面板
- 页面上随便点一个功能,比如商品列表
- 找到一个
/api/product/list之类的请求URL - 在前端工程的
src/api目录里搜索这个路径,找到对应的请求函数 - 再根据这个URL映射到后端的Controller,看它调用了哪个Service
- 顺着Service找到Mapper,最后看到真正的SQL
这一条链路走下来,你就知道"页面上某个数据"是从哪张表的哪个字段来的了。改前端样式去src/views下找vue文件,改业务逻辑去Service里改,改查询条件去Mapper里改SQL。五分钟就能定位到要改的文件,你就不慌了。
6.2 三个最常见的改造点
第一个改造点是首页内容。很多商城项目的首页轮播图、公告、推荐位,要么是后台一张banner表,要么是前端vue文件里写死的数据。改起来不难,找到对应的表或组件,替换成自己的内容就行。
第二个改造点是商品搜索。如果项目本身没有搜索功能,最简单的方案是在商品列表接口加一个keyword参数,SQL改成:
SELECT * FROM product WHERE status = 1 AND name LIKE CONCAT('%', #{keyword}, '%') ORDER BY create_time DESC前端搜索框绑定一个input,提交时把关键词当作请求参数发过去即可。
第三个改造点是订单支付。标准商城源码通常只做到"下单生成订单、状态待付款",真正的支付需要接入第三方支付平台的回调。系统里通常已经预留了pay_type、pay_time字段,你要做的就是写一个回调接口,校验支付结果的签名,然后把订单状态从0改成1,记录支付时间。这块涉及具体的支付平台接入,每家平台的对接文档都不一样,但整体思路基本都是"回调 -> 验签 -> 更新订单状态"。
6.3 接口速查表
跑通之后,建议对照这张接口表理解前后端是怎么咬合的:
| 方法 | 路径 | 功能 | 权限 |
|---|---|---|---|
| POST | /api/user/login | 用户登录 | 公开 |
| POST | /api/user/register | 用户注册 | 公开 |
| GET | /api/product/list | 商品分页列表 | 公开 |
| GET | /api/product/detail/{id} | 商品详情 | 公开 |
| POST | /api/cart/add | 加入购物车 | 登录 |
| GET | /api/cart/list | 购物车列表 | 登录 |
| POST | /api/order/create | 创建订单 | 登录 |
| GET | /api/order/list | 我的订单列表 | 登录 |
| PUT | /api/admin/order/status | 修改订单状态 | 管理员 |
也就是说,前端页面本质上是一堆接口的调用方,后端接口是一堆业务的出口。你要改页面,先找页面调的接口;你要改接口,先找接口对应的Service。接口是前后端之间的契约,搞清楚这张表,等于拿到了这套源码的地图。
6.4 改皮肤和加页面
换主题色是很多人拿到源码后想做的第一件事。如果前端用了Element UI,一般在main.js或样式变量文件里能找到主题相关配置。Element UI的或Element Plus的主题定制思路都是改CSS变量,搜索primary这个关键字,找到全局主色值,改掉就行。如果项目用了SCSS,那就在styles/variables.scss里改,全局生效。
新增页面也不难:在src/views下复制一个现有页面,改掉内容,然后在src/router里注册一条新路由,最后在侧边栏菜单配置里加一项。三步走下来,一个新页面就挂上去了。这里面最需要注意的是新增页面的路径和权限配置要跟菜单、路由保持一致,少配一项就可能会出现"路由存在但访问404"或者"菜单点了没反应"的情况。
7. 按出现频次排好的运行问题清单
7.1 高频问题排查表
把我和几位同学的实测过程汇总一下,下面这些问题出现频率最高:
| 问题现象 | 真实原因 | 处理办法 |
|---|---|---|
| 后端启动失败,提示Failed to configure a DataSource | 数据库没启动,或连接参数没改 | 先启动MySQL,核对application.yml里的账号密码库名 |
| 提示Unknown database 'mall' | SQL脚本没导入,或库名不一致 | 执行create database后再导入脚本 |
| 提示Access denied for user 'root' | 数据库密码和yml配置不一致 | 统一两者密码 |
| 提示Public Key Retrieval is not allowed | MySQL8需要额外连接参数 | 在url末尾加allowPublicKeyRetrieval=true |
| 前端npm run serve报openssl错误 | Node版本太高 | 降到Node 16,或加NODE_OPTIONS=--openssl-legacy-provider |
| node-sass安装失败 | node-sass与Node版本不匹配 | 切到Node 14/16,或用dart-sass替代 |
| 前端请求接口CORS报错 | 后端开了跨域但配置不完整,或前端baseURL写绝对路径 | 后端用AllowedOriginPattern,前端baseURL改成/api |
| 登录成功后所有接口都401 | 拦截器白名单没放行,或前端没带Token | 检查excludePathPatterns和Axios请求头 |
| 后台上传图片访问404 | 静态资源目录没有映射 | 配置addResourceHandlers指向上传目录 |
| 接口能通但返回数据为空 | 后端字段是下划线,前端驼峰,没做映射 | 开启mybatis map-underscore-to-camel-case,或SQL里写别名 |
这十类问题里,前四个都是数据库层面的,直接印证了我前面反复说的:数据库连接是后端启动的第一关。这一关过了,整个系统基本就稳了一半。
7.2 我的排查顺序
拿到别人给的"可直接运行"项目,我自己的排查顺序是固定的,分享给你参考:
第一,先看后端日志,看第一个报错是什么。后端日志是所有问题的放大镜,它已经把原因写在里面了,只是很多人没耐心读。
第二,用数据库客户端单独连一次MySQL。能连上,说明账号、密码、端口没问题;连不上,后面做什么都是白费。
第三,后端起来后,先用浏览器或接口测试工具访问一个公共接口,确认后端对外可用。
第四,前端起来后,打开Network面板,看第一个页面请求返回什么状态码。404是路径或代理问题,401是权限问题,500是后端异常,CORS就是跨域配置问题。
第五,每个环节只验证一条完整链路:页面 -> 前端代理 -> 后端Controller -> Service -> Mapper -> 数据库。链路只要通了,后面再遇到问题,就都是普通问题。
我自己的习惯是:拿到任何一套SpringBoot加Vue的"可直接运行"项目,第一件事不是读业务代码,而是先把版本对齐、把数据库导入、把后端拉起来、把前端代理配好,确认一条最基本的请求链路通了之后,再开始看功能。链路通了,项目的基本盘就稳了,后面所有问题都会变成可定位、可解决的普通问题。这套商城系统的价值也正在于此:它是一个能完整跑通的真实业务闭环,而不是一堆"看起来能跑"的代码片段。把这条链路上每一环都吃透,一个全栈项目的骨架,你就真正拿到手里了。