如果你是一个准备毕业设计、刚入行做全栈,或者想在自己电脑上完整跑通一套电商系统的人,这个项目标题应该已经眼熟很久了。SpringBoot + Vue + MyBatis + MySQL,前后端分离,还带完整源码和部署教程,这几个关键词组合在一起,几乎就是JavaWeb全栈项目的标准答案。但标准答案不代表没有坑,我在帮朋友和自己调试这类项目的过程中,踩过的环境版本问题、跨域问题、数据库初始化问题,至少够写十篇排错记录。这篇就按一个完整项目的视角,把这个电商平台的系统设计、技术选型、本地运行、部署上线和典型故障排查,从头到尾捋一遍。
这套系统的核心价值在于:它不是那种把前端页面和后端接口写在一个工程里的传统单体项目,而是真正的前后端分离架构。前端用Vue管理页面交互和路由,通过axios这种HTTP客户端去请求后端接口;后端用SpringBoot提供RESTful API,配合MyBatis操作MySQL数据库。两边通过JSON格式的数据交换,各跑各的进程,甚至各部署各的服务器。这种模式的好处,你开发的时候体会最深:后端写完接口不用管页面长什么样,前端拿到接口文档就能并行开发,联调阶段才碰头,效率比传统JSP那套高太多。
1. 项目整体设计与核心思路拆解
1.1 技术栈为什么是SpringBoot + Vue + MyBatis + MySQL
先说后端。SpringBoot之所以成为Java后端的事实标准,是因为它把Spring家族那一堆繁琐的XML配置全部自动化了。你现在创建一个SpringBoot项目,一个main方法就能启动内嵌的Tomcat,不用再去装独立的Web服务器,不用写web.xml,依赖管理交给starter机制,引入一个spring-boot-starter-web就有了一套完整的MVC能力。这就是为什么它适合做课程设计和中小型项目——你不需要在环境搭建上消耗太多精力,把时间花在业务逻辑上。
Vue这边,它的核心优势是响应式数据绑定和组件化开发。页面上的数据变了,视图自动更新,你不用再像写jQuery那样手动操作DOM。组件化带来的好处更明显:一个电商系统的导航栏、商品卡片、购物车列表、订单详情,都能拆成独立组件,各自维护各自的逻辑,页面之间通过路由跳转,组件之间通过props和事件通信。对于电商这种页面多、交互复杂的场景,Vue的开发效率确实比传统模板引擎高出一大截。
MyBatis的选型则体现了一个务实原则:在电商系统这种SQL逻辑复杂的场景里,手写SQL比ORM自动生成的SQL更可控。商品列表要做多条件筛选、订单要做多表联查、统计报表要做聚合查询,这些场景下MyBatis的<select>标签能让你精准控制每条SQL,配合<resultMap>手动映射查询结果,数据怎么查、怎么封装,全部心里有数。Java面试里MyBatis也是高频考点,用它做项目,面试官问起来你至少能说出个一二三。
1.2 前后端分离到底解决了什么问题
我跟不少初学者聊过,他们对“前后端分离”这个概念的理解就是“前端一个文件夹、后端一个文件夹”,这理解太浅了。真正的分离,本质上是关注点分离和部署方式分离。
关注点分离,说的是前端专注于页面渲染和用户交互,后端专注于业务逻辑和数据处理,两边只通过HTTP接口通信。这样前端人员不需要懂Java,后端人员不需要会写CSS,团队协作效率高很多。部署方式分离更实际:前端构建完是一堆静态文件,扔到Nginx上就行;后端打包成一个jar包,跑在服务器上。两边可以独立扩容——访问量大了前端可以加CDN,接口压力大了后端可以扩展实例,互不影响。
这套设计方案里还有一层隐藏逻辑:接口先行的开发模式。前端页面需要什么数据,大家先约定好接口格式,然后前后端并行开发。我在实际项目中体会特别深,接口定义好了,后端mock数据,前端mock接口,双方能同时干一个月的活儿,最后联调一个星期搞定。这种节奏是传统单体项目完全给不了的。
2. 系统核心功能与数据库设计详解
2.1 电商系统的模块划分
一个电商平台的主体逻辑,拆开来看基本就是几个核心链路:用户登录注册、商品浏览检索、购物车管理、下单支付、订单管理。每个链路就是后端的一个模块,前端对应一组页面。
用户模块负责注册、登录、个人信息维护。登录这里我强烈建议用JWT,不用传统的Session。前后端分离架构下,后端接口可能是给PC端Web、也可能是给小程序、App共用,Session存在服务器内存里没法跨端共享,而JWT本身就是一段携带用户信息的加密令牌,前端拿到后存在localStorage里,每次请求带上,后端验签通过就放行。无状态,天然适配多端场景。
商品模块是电商系统的门面。简单一点的系统就做成单级分类,进阶一点的做两级分类。商品信息至少包含标题、主图、轮播图列表、详情描述、价格、库存、销量、上下架状态。检索这块,通常做的是关键字模糊查询加分类过滤,SQL里就是WHERE title LIKE ? AND category_id = ?,简单直接。
购物车和订单模块是整个系统的业务核心,也是最容易出问题的地方。购物车本质上是一张关联用户和商品的中间表,难点在数量变更和价格同步。订单模块则涉及事务操作——下单时不仅要插入订单主表和订单明细表,还要扣减库存、清空购物车,这几步必须在一个事务里完成,任何一步失败都得整体回滚,不然就会出现订单创建了库存没扣、钱付了货没发的问题。
2.2 数据表设计与字段规划
数据库设计这个环节,直接决定你后面写SQL的难度。我见过不少初学者上来就建表,字段能省就省,结果做到订单模块发现没地方存收货地址,得回去改表,连带实体类、Mapper全要改,极其痛苦。合理的做法是先画清楚表之间的关系再动手。
在标准的电商系统中,核心涉及的表通常是这几张:
| 数据表 | 核心字段 | 关联关系 |
|---|---|---|
| t_user 用户表 | id, username, password, nickname, avatar, phone, email, create_time | 与订单一对多 |
| t_category 分类表 | id, name, parent_id, sort, status | 自关联实现两级分类 |
| t_product 商品表 | id, category_id, name, subtitle, main_image, sub_images, detail, price, stock, sales, status | 与分类多对一 |
| t_cart_item 购物车表 | id, user_id, product_id, quantity, checked | 与用户、商品各多对一 |
| t_order 订单表 | id, order_no, user_id, receiver_name, receiver_phone, receiver_address, total_amount, pay_status, order_status, create_time | 与用户多对一 |
| t_order_item 订单明细表 | id, order_id, product_id, product_name, product_image, price, quantity, total_amount | 与订单多对一 |
这里有几个细节需要注意。product_name和product_image这种看上去冗余的字段,其实是刻意为之的。订单明细必须把商品名称和图片存一份快照,因为商品信息将来可能修改、也可能下架,如果关联查询实时取商品表,历史订单显示就会对不上。这就是电商系统里的一个经典设计——订单数据落库后自成体系,不依赖业务表的实时数据。
订单号和主键id要区分开,主键id是自增的数字,订单号则是业务编号,通常用时间戳加随机数生成,如202506071530120001。订单号要对外展示、要支持搜索,所以必须建唯一索引。
还有一个关键词搜出来很多:MySQL安装、MySQL配置。这个项目建议使用MySQL 8.x版本,注意字符集在初始化连接时显式指定useUnicode=true&characterEncoding=utf8,否则中文数据可能乱码。
2.3 MyBatis的核心用法与配置细节
MyBatis在这个项目里的用法,掌握三个层次就够了:XML映射文件、动态SQL、注解。
XML映射文件是MyBatis的灵魂。每个Mapper接口对应一个XML文件,接口方法名和XML里语句的id对应。比如查询商品列表的多条件筛选,<where>标签自动处理多余的AND关键字,这个细节救过不少人的命:
<select id="selectProductList" resultType="com.example.entity.Product"> SELECT * FROM t_product <where> <if test="categoryId != null"> AND category_id = #{categoryId} </if> <if test="keyword != null and keyword != ''"> AND name LIKE CONCAT('%', #{keyword}, '%') </if> AND status = 1 </where> ORDER BY sales DESC </select>动态SQL是MyBatis最实用的能力。多条件组合查询在电商系统里太常见了,用户可能在分类页选中一个价格区间,同时又输入关键字筛选。<if>加<where>的组合,能让你摆脱拼接SQL字符串的噩梦。
配置层面有几个点要检查。第一个是驼峰映射:数据库字段是下划线风格(receiver_name),Java实体是驼峰风格(receiverName),在application.yml里加一行map-underscore-to-camel-case: true就能自动转换,省去大量手写resultMap的体力活。第二个是日志打印,开发阶段配置mybatis.configuration.log-impl: org.apache.ibatis.logging.stdout.StdOutImpl,在控制台能看到每条SQL和执行参数,排查问题全靠它。
MyBatis缓存也是面试常考点,这里简单说下。一级缓存是SqlSession级别的,默认开启,同一个SqlSession内查询相同SQL会走缓存。二级缓存是namespace级别的,需要在XML里手动开启<cache/>,但注意脏读问题——如果多个表关联查询,任何一个表更新了数据,相关的二级缓存都可能失效。做电商系统建议先不开二级缓存,列表查询加上合理的数据库索引,性能就够用了。
3. 环境准备与项目运行实操
3.1 版本搭配:JDK、SpringBoot、Node这些怎么选
版本问题是我从各种搜索热词里看到的第一个坑。很多人拿到一套源码,直接在自己的环境里跑,报错报得莫名其妙,最后发现是版本不匹配。就这套SpringBoot + Vue项目,我给出一个现在最稳妥的搭配方案:
JDK用8或11,SpringBoot用2.x(比如2.7.x),MyBatis用spring-boot-starter整合包,MySQL用8.0或5.7。这个组合经过了最多项目的验证,资料也最全,哪怕出问题,CSDN和GitHub上一搜就能搜到解决方案。
SpringBoot 3.x现在是出来了,但注意3.x是基于JDK17的,如果你本机还是JDK8,强上SpringBoot 3.x必然会报错。不要盲目追新,课程设计和项目实战最重要的是稳定复现。前端方面,Vue就用2.x版本,配Node 14或16即可。Vue 3现在已经很主流,如果说你拿到的源码本身就是Vue 3的,那Node版本建议16以上,Vite构建工具要求Node更高版本,npm安装依赖的时候注意看报错提示。
Maven建议用3.8.x,配置阿里云镜像仓库,不然SpringBoot依赖下载能卡到你怀疑人生。设置方式是在settings.xml里加镜像mirror,把中央仓库替换成https://maven.aliyun.com/repository/public。这一步非常关键,我第一次搭环境啥都没配,拉一个SpringBoot依赖花了半小时,配好镜像后五分钟搞定。
3.2 数据库初始化:导入SQL脚本的那些细节
源码包里一般会附带一个sql文件夹,里面是建库建表的脚本,通常是ecommerce.sql之类的名字。导入之前先看两遍脚本内容,确认里面有没有CREATE DATABASE语句,有的话需要修改密码或库名。没有的话,先手动创建数据库,再指定库导入。
导入的步骤,我推荐用命令行,比图形化工具更可靠:
mysql -u root -p CREATE DATABASE ecommerce DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; exit; mysql -u root -p ecommerce < ecommerce.sql注意数据库字符集一定要显式指定utf8mb4,不要直接用默认的latin1。很多人的中文乱码问题就是从这里开始的。导入完成之后,进数据库抽查几条数据,看中文是否正常显示、表数量是否正确。特别是表数量,脚本可能建了十几张表,导入时漏掉一张,后面项目运行时一查这张表就报错。
如果脚本文件很大,用Navicat导入容易超时或者报错,命令行反倒是最稳的。这里我踩过一次坑:用可视化工具导入一个100MB的SQL文件,跑到70%直接闪退,数据库里留下了一堆半截表,清理起来比重新导入费劲得多。
3.3 后端启动完整流程
后端项目的启动流程,核心是三步:改配置、装依赖、跑起来。
第一步,打开application.yml或application.properties,把数据库连接信息改成你自己本机的。核心配置项就这几行:
server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/ecommerce?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 你自己的密码 driver-class-name: com.mysql.cj.jdbc.Driver这里有个参数serverTimezone=Asia/Shanghai,必须加。MySQL 8.x的时间类型和Java默认时区不一致,不加这个参数,后端的日期时间字段会整体差8个小时,你查订单创建时间会发现全部对不上。用MySQL 5.7的时候,driver-class-name是com.mysql.jdbc.Driver,MySQL 8.x必须用com.mysql.cj.jdbc.Driver,这个也经常有人配错。
第二步,用Maven或IDEA导入依赖。IDEA打开pom.xml,等待右下角进度条跑完,这个时候不要干别的,就去检查依赖有没有报红。常见的坑是spring-boot-starter-parent的版本号在中央仓库找不到,或者某个依赖下载超时失败。一般重新点击Reload All Maven Projects按钮就能解决。
第三步,找到主启动类(类名带Application后缀,有@SpringBootApplication注解),直接运行main方法。控制台出现SpringBoot的Banner,然后看到Tomcat started on port(s): 8080,说明后端起来了。验证接口,浏览器访问http://localhost:8080/swagger-ui.html看有没有接口文档页面,或者访问某个公开的接口如http://localhost:8080/api/product/list看有没有返回JSON。
3.4 前端启动完整流程
前端的坑比后端更多,集中爆发在依赖安装阶段。
拿到前端代码,先看有没有package.json,有的话说明是个标准的前端工程。首先确认自己Node环境版本,命令行里node -v、npm -v各查一遍。版本没问题后,执行:
npm install这一步等的时间可能很长,npm默认源在国外,网络稍差就卡在中间。国内直接换淘宝镜像:
npm config set registry https://registry.npmmirror.com然后再跑install,速度会快很多。安装过程如果报错,把node_modules文件夹整个删掉重来,不要在一个坏掉的环境上反复修补,浪费时间。
依赖装完之后,看源码里的接口配置。前端项目通常在src/utils/request.js或src/api/目录下配置了axios实例,里面有baseURL:
const service = axios.create({ baseURL: '/api', // 开发环境走代理 timeout: 5000 })如果用/api这种相对路径,说明项目配置了代理转发,查看vue.config.js或vite.config.js里的devServer配置,确认代理指向的后端地址是正确的:
devServer: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true, pathRewrite: { '^/api': '' } } } }这里配好了,前后端之间就不存在跨域问题,因为浏览器看到的是同源的地址。最后执行:
npm run dev浏览器显示Vue的启动日志,访问http://localhost:8081,能看到登录页。用初始化脚本里的测试账号登录,整个链路就跑通了。
4. 前后端接口联调与登录鉴权实现
4.1 登录认证流程的完整设计
前面提到用JWT做登录认证,这里把完整流程展开说。用户输入账号密码提交给后端,后端拿到后先查t_user表,比对密码。注意数据库里存的密码一定不能是明文,要有加密过程,通常用BCrypt算法加密再落库,SpringSecurity自带这个工具类。比对通过之后,生成一个JWT令牌,令牌里放入用户id和用户名,设置过期时间,比如7天或24小时,然后返回给前端。
前端收到令牌后,把令牌存到localStorage里,后续每次发请求,都在请求头里带上:
service.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers['Authorization'] = token } return config })后端这边,就需要写一个拦截器,拦截需要登录的路径。登录接口和注册接口是白名单,其他接口都要校验令牌。校验过程就是取出请求头里的Authorization字段,用JWT工具类解析,验签通过就放行,把用户信息存入请求上下文;验证失败就返回401状态码,前端收到401就跳回登录页。
这个方案最大的好处是无状态,后端不需要维护Session,所以你重启服务前端不会掉登录(JWT本身没过期就行)。分布式部署场景下,多个后端实例都能验证同一个令牌,因为它们用的是同一个密钥。对称密钥的配置,可以放在application.yml里,也可以放到Nacos这类配置中心,看你的项目复杂度。
4.2 axios封装与拦截器开发的思路
前端请求的封装质量,直接影响联调阶段的心情。我见过很多项目直接在组件里this.$http.get()一把梭,出了问题全在组件里改,维护痛苦。规范的做法是抽出统一的http工具模块,在上面加拦截器,这样token注入、错误提示、状态码处理都在一个地方完成。
一个合格的拦截器应该处理这些情况:请求拦截阶段注入token;响应拦截阶段做状态码判断,200正常返回数据,401跳登录页清除本地用户信息,500统一弹提示;网络超时错误单独提示;关键接口的loading状态提示。把这些逻辑全部收敛到一处,组件里就只管业务回调,代码干净很多。
4.3 解决跨域问题的三种方案
前后端分离项目跑不起来,八成是跨域问题。什么是跨域?浏览器安全策略规定,你的页面在localhost:8081,它访问localhost:8080的接口,因为端口不同,属于跨域请求,浏览器默认会拦截响应。解决方案有三种,我按推荐顺序排个序。
第一种是前端开发阶段的代理方案,前面说的vue.config.js配置devServer.proxy,浏览器看到的请求是同源的,由本地开发服务器转发到后端,速度最快,不需要后端做任何调整。缺点是本地生效,生产环境不适用。
第二种是后端启用CORS跨域支持,加一个配置类或@CrossOrigin注解,允许指定的前端域名访问。不能让所有域名都放行,生产环境这个配置要收紧到具体域名。优点是前后端各管各的,缺点是生产环境要求前端页面和后端接口同域,那就还有第三种方案。
第三种是生产环境部署时用Nginx做反向代理,前端静态文件和接口路由都挂在同一个域名下,通过location /api把请求转发到后端服务。浏览器看的就是同源的,跨域问题从根上消失。这不仅是解决跨域的方案,也是生产环境的标准部署形态,下面是完整的Nginx配置示例。
5. 生产环境部署:从本地到线上
5.1 后端打包:jar包方式部署
后端代码开发完毕后,打包这一步可以用IDEA的Maven面板执行package命令,跳过测试用mvn clean package -DskipTests。构建成功后,target目录下会生成一个xxx.jar包,这个jar是可执行的FatJar,里面包含了所有依赖和内置的Tomcat。
输送到服务器前,先注意两件事。第一是修改生产环境的配置,数据库地址指向服务器上的MySQL,服务器IP或云数据库连接地址,数据库密码用生产环境单独设的强密码。可以加一个application-prod.yml的配置文件,用spring.profiles.active=prod来激活,这样开发测试和生产环境配置互不干扰。
上传jar包到服务器后,启动命令:
nohup java -jar ecommerce.jar --spring.profiles.active=prod > app.log 2>&1 &nohup加&让进程后台运行,输出重定向到app.log方便看日志。检查是否启动成功,直接看日志文件的关键词“Started”。
服务器内存有限的话,可以限定JVM内存大小,比如-Xms256m -Xmx512m,小配置服务器跑这个项目完全够用。在实际的部署过程中,服务器上只安装JDK和MySQL,不用装Maven和Node,构建这步本地完成,服务器只负责运行。
5.2 前端打包与Nginx部署
前端打包更简单:
npm run build构建完成后,dist目录下就是纯静态文件。把这堆文件上传到服务器某个目录,比如/usr/share/nginx/html或你自己定义的路径,再用Nginx托管。
Nginx配置是整个部署环节的核心。它要做三件事:托管前端静态文件、把API请求反向代理到后端端口、处理SPA路由的history模式刷新404问题。一个实测可用的配置:
server { listen 80; server_name your-domain.com; root /usr/share/nginx/html; index index.html; # 前端路由 history 模式配置:找不到文件时回退到 index.html location / { try_files $uri $uri/ /index.html; } # 接口反向代理 location /api/ { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }这里有个容易搞错的点:proxy_pass http://127.0.0.1:8080/;末尾的斜杠非常关键,带斜杠表示把/api前缀去掉再转发给后端,比如请求/api/product/list转发给后端的实际路径是/product/list。如果不带斜杠,转发过去还是/api/product/list,而后端接口定义里没有这个前缀,就会404。所以你的后端有没有统一加context-path,Nginx这里是带slash还是不带的规则,要配合起来。
SPA的history模式路由,痛点在于前端路由是浏览器端控制的,如果直接用/product/detail/1这样的地址访问,Nginx在服务器上找不到这个路径的文件,会返回404。try_files的配置让Nginx把这种请求全部回退到index.html,由前端路由接管,问题解决。
5.3 云服务器部署步骤梳理
如果用的是云服务器,流程就是:安装JDK和MySQL、导入数据库SQL文件、上传jar包和dist目录、配置Nginx、启动后端、放行安全组端口。这里面最容易遗漏的是安全组规则,很多人在服务器内部配了半天,外部访问还是不通,最后发现是云控制台的安全组没放行80端口和8080端口。40端口放行后,访问http://服务器公网IP,网站就出来了。
部署过程中,日志排查是核心技能。后端日志看app.log,报错信息基本都能定位。数据库连接不上,去服务器本地mysql -u测试;端口被占用,netstat -tlnp看监听状态;防火墙拦截,检查iptables或firewalld状态。生产环境不要用root直接跑java进程,单独建一个应用用户比较稳妥,权限隔离能避免很多低级安全隐患。
6. 常见问题与排错经验,踩坑实录
6.1 启动报错与排查思路速查表
我在跑电商平台这种项目时,最常遇到的报错,基本就集中在这张表里面,你可以收藏备用:
| 现象 | 直接原因 | 解决方案 |
|---|---|---|
| 后端启动报数据库连接失败 | 密码错误/库名错误/MySQL未启动 | 检查application.yml连接参数,命令行手动测试连接 |
| 查询数据全是中文乱码 | 数据库字符集不是utf8mb4 | 重建数据库,指定DEFAULT CHARACTER SET utf8mb4,连接URL加characterEncoding |
| 前端请求403/401 | Token未传/过期/拦截器配置错误 | 清空localStorage重新登录,检查axios拦截器 |
| 前端请求500 | 后端接口异常 | 查看后端控制台日志,按异常堆栈定位 |
| npm install卡住 | 网络问题 | 切换淘宝镜像,删除node_modules重来 |
| Maven依赖下载慢/失败 | 中央仓库网络不通 | 配置阿里云镜像,IDEA执行Reload重新加载 |
| 数据访问报表不存在 | 数据库表没建/名字对不上 | 检查SQL脚本是否完整导入,核对表名 |
| 端口被占用 | 上一个进程没杀干净 | `netstat -ano |
6.2 数据访问常见报错与原因分析
数据访问这块的报错,是我个人看到求助最多的一类。有个我印象很深的报错:
java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax...这种报错十有八九是SQL里的某个字段名碰撞了MySQL关键字,最常见的是order。你建了一个订单表就叫order,或者有个字段叫order,SQL里查它就会炸。解决办法:要么表名和字段名加反引号转义,用\order`这种写法;要么建表时改成t_order或者order_no`这种不冲突的名字。我的建议是后者,一劳永逸,不用到处加反引号。
还有一类是MyBatis映射时报错,最常见的错误写的是There is no getter for property named 'xxx'。这个一般是实体类的字段名和参数占位符#{xxx}不一致,或者MyBatis的驼峰映射开关没打开。检查实体类字段名、XML里的参数名、配置开关三处,基本能定位。
这类项目里事务问题也逃不掉,下单接口必须加@Transactional事务注解。如果你测试时发现库存扣成功了但订单表没有数据,十有八九就是事务没加或者没生效。@Transactional要加在public方法上,而且不能同类内自调用,这也是一个经典的失效陷阱。排查事务问题用数据库慢查询日志或者加监控看更直观。
6.3 前端常见问题:依赖、路由与播放文件
前端报错我看热词里有一堆和Vue配置相关的问题,比如有人问“vue项目源码怎么发给别人”,其实就是把项目压缩打包,对方拿到后npm install再npm run dev就能跑。但注意不要发送node_modules目录,那个文件夹动辄几百兆,而且对方本机环境不同,发给别人也没法用。把源码package.json带上,依赖让对方自己装。
Vue Router路由配置这块,一个常见问题是不管访问什么路径都渲染同一个组件,检查是否忘记在路由表里加上path: '*'对应404页面,或者路由配置的routes数组元素顺序放错了。更常见的坑是history模式刷新后404,这个前面已经说清楚了,Nginx配try_files解决。
我还在热词里看到一个有点意思的:“vue播放欢乐谷m.3u8”。这本质上是Video.js或hls.js播放m3u8视频流的问题,跟电商项目本身关系不大,但如果你在商城系统里做了一个视频播放功能,比如商品详情页放一段宣传视频,遇到m3u8格式,就需要在Vue项目里引入hls.js这个库来转换播放。轻量级的方式是用video.js配合Vue的封装组件,视频地址配置成m3u8文件路径就行。这个功能对商品展示挺加分的,可以作为一个扩展点。
6.4 数据库性能优化与索引调优建议
项目能跑起来只是第一步,做一个像样的电商系统,数据库这块还得折腾折腾。商品表查询业务占了绝大多数,所以索引要重点照顾。category_id必建普通索引,name字段做模糊查询用LIKE '%关键字%'这种写法肯定是没法走索引的,可以考虑给商品名字段加全文索引,或者引入Elasticsearch做搜索,但这个对于起步项目有点重了,可以把搜索改成LIKE '关键字%'的前缀匹配,勉强能走索引。
订单表按用户查订单,user_id必建索引;订单号order_no有唯一性要求,建唯一索引。每次下单扣库存时候,注意是更新t_product表里的stock字段,要在事务里配合条件更新,比如UPDATE t_product SET stock = stock - #{count} WHERE id = #{productId} AND stock >= #{count},这个带条件判断的更新语句能防住超卖,比直接stock - 1安全得多。
分页查询用MyBatis的PageHelper插件,一个依赖一个配置就能用:PageHelper.startPage(pageNum, pageSize)开启分页,紧跟其后的查询自动带上LIMIT。PageHelper这东西有个坑,就是只对紧跟着的第一条SQL生效,所以startPage后面不能隔别的代码,不然分页会失效或作用到别的查询上,实测踩过多次。
热词里还有“mybatis二级缓存实现”,如果真研究这块,建议阅读官方文档的 标签部分,但要牢记我之前说的,涉及关联表的场景要慎用。缓存带来的性能提升在这个体量的项目里感知不强,但缓存失效导致的数据不一致,坑你排查一整晚都是有可能的,先把正确性做出来再谈性能。
7. 项目运行与排错经验总结
这个电商项目我前前后后帮人调过不下十次,每次拿到手第一件事永远是先看版本、再看配置、最后才看代码。版本不对,代码再对也跑不起来;配置不对,服务能启动但功能全报错;代码问题反而是最好定位的,毕竟报错信息会直接告诉你哪一行出事了。
有几个实操习惯真的值得从第一天就养成。第一个,数据库脚本导入前养成备份习惯,线上环境尤其重要,别直接拿不知道内容的SQL脚本在正式库上跑。第二个,后端配置文件里的密码和密钥不要硬编码,用SpringBoot的@ConfigurationProperties配合环境变量或者号配中心去管理,就算你是部署在自己的小服务器上,这个习惯也能给你以后做项目省下大麻烦。第三个,前端每次改动后跑一遍npm run build确认能构建成功,别拖到要部署了才发现代码带了一堆编译错误,这坑我已经替大家踩过了。
如果只想实现功能,跟着这篇文章的节奏就行,从建库开始,一步一步跑通联调,应该半天就能在浏览器里看到一个完整的电商系统,前后端各自工作、数据流动起来。写这类系统,跑通流程永远只是第一步,把接口设计得更规范,把事务边界想得更清楚,把索引建得更合理,这些才是这段经历真正沉淀下来的东西。
最后多分享一个我自己的调试习惯:遇到前端问题,先打开浏览器F12看Network面板;遇到后端问题,先看控制台第一条Exception堆栈,不往下翻。绝大多数问题,这两个动作一做完,方向就定了。别在页面样式上怀疑后端逻辑,也别在后端日志里找前端错误,前后端分离的项目,排查思路也要跟着分层走,这样效率才是最高的。