做Java Web项目最怕的不是写不出代码,而是整套东西东拼西凑,前端一套、后端一套,数据库脚本还没有,最终卡在联调上。这套手机销售网站系统源码,技术栈直接锁定SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0,前后端分离,还带文档和数据库脚本,属于拿来就能跑、跑起来就能改的那类项目。下面我就按“选型思路→功能拆解→表结构设计→启动实操→问题排查→二次开发”这条线,把这套系统从头到尾捋一遍,帮你看清每个模块到底怎么落地。
1. 项目整体设计与思路拆解
1.1 为什么是这套技术栈
先聊技术选型。这套项目用的是SpringBoot 2.x而不是3.x,很多人问为什么不用新版。核心原因是生态和兼容性。SpringBoot 3.x强制要求JDK17起步,不少老插件、老教程、公司内部依赖都还停留在JDK8的节奏上,而SpringBoot 2.x在JDK8和JDK11下都跑得很顺畅,资料多、坑少、网上能搜到的解决方案也全。对于一个供学习、毕设或二次开发使用的项目来说,稳比新重要。
前端用Vue3是顺理成章的事。Vue3的组合式API(Composition API)把逻辑聚合得更干净,配合Vite开发服务器,热更新速度快,开发体验比Webpack那套舒服很多。项目里一般还会搭配Element Plus做后台管理界面,表单、表格、弹窗、分页这些现成组件直接拿过来用,省掉大量重复样式代码,界面也能做到基本工整。
MyBatis-Plus则是MyBatis的增强版,它存在的意义就是少写SQL。单表CRUD、分页查询、条件构造器这些,靠BaseMapper和LambdaQueryWrapper就能搞定,代码量直接砍半。尤其手机销售网站这种典型业务管理系统,绝大多数操作都是单表增删改查,MyBatis-Plus的匹配度非常高。注意,连表查询仍然需要手写SQL,比如订单详情要关联商品表和用户表,这部分跑不掉。
MySQL8.0作为数据库是当前主流,字符集默认utf8mb4,能存emoji,也支持窗口函数和公用表表达式。手机商品描述里经常有特殊符号,utf8mb4能避免很多编码坑。整体来看,这套技术栈没有炫技成分,全是最常见、资料最多的组合,目标很明确:让项目能被快速理解、快速启动、快速改造成自己需要的样子。
1.2 手机销售网站的业务模型
手机销售网站本质上是一个典型的B2C电商场景,核心业务链路是:用户注册登录→浏览商品列表与详情→加入购物车→提交订单→后台处理订单。前端面向普通用户,提供分类浏览、搜索、商品详情、购物车、个人订单等功能;后端面向管理员,提供商品管理、分类管理、订单管理、用户管理等。
比起其他电商项目,手机销售有一个特点:商品参数维度固定且可枚举。比如品牌、运行内存、存储容量、屏幕尺寸、电池容量、摄像头像素,这些字段是固定的,用户筛选时也是按这些维度组合。所以数据库设计时,手机参数建议用独立字段或JSON字段存储,而不是塞进一大段富文本里,否则筛选和排序会非常痛苦。
整个系统的角色权限也简单,用户和管理员两类即可。用户端不开放注册管理员的入口,管理员账号由数据库预置或者由已有管理员创建,这样权限边界清晰,开发量也小。订单状态一般就是待付款、待发货、已发货、已完成、已取消几个枚举值,后台通过状态流转来驱动业务,前端根据状态显示对应按钮。
1.3 前后端分离与项目目录结构
这类项目采用前后端分离架构,典型目录安排是:后端一个SpringBoot工程,前端一个Vue3工程,两者通过HTTP接口通信。
后端工程里,按分层架构组织:
- controller:接收前端请求,做参数校验和响应封装
- service:业务逻辑层,处理订单、库存、登录等核心业务
- mapper:数据访问层,继承BaseMapper,配合MyBatis-Plus使用
- entity:数据库实体类,对应每张表
- config:配置类,比如跨域配置、MyBatis-Plus分页插件配置
- common:统一返回结果、异常处理、JWT工具类等
前端工程里,路径结构也很有规律:
- views:页面组件,按用户端和管理端拆分子目录
- router:路由配置,带登录守卫
- store:状态管理,存token和用户信息
- api:接口封装,统一管理所有请求地址
- utils:axios实例封装、格式化工具等
前后端分离模式下,开发环境前端跑在5173端口,后端跑在8080端口,跨域问题通过Vite的proxy代理解决。生产环境则把前端打包成静态文件后,交给Nginx托管,再由Nginx把/api前缀的请求转发到后端服务,这种部署方式结构清晰,也方便后续扩容。
2. 核心功能拆解与关键实现细节
2.1 数据库表设计思路
一套电商系统要跑通,表结构是地基。手机销售网站一般至少需要这几张核心表:用户表、商品分类表、商品表、购物车表、订单表、订单商品明细表。订单明细表必须单独建,因为订单里的商品快照和商品表不是一回事。
用户表重点字段包括:用户名、密码、手机号、头像、角色标识。密码这里特别提醒,千万别存明文,建议使用BCrypt加密,哪怕项目只是学习用,也该养成这个习惯。
商品表是所有表里字段最多的:
- 基础字段:商品名称、所属分类、主图、详情图文、价格、库存
- 销量统计:销售数量、浏览数
- 状态字段:上架/下架开关
- 规格参数:品牌、内存、存储、颜色等
库存字段的设计要格外小心。手机这种高客单价商品,用户不会像买日用品那样一次买几百件,但依然存在并发扣减问题。下单扣库存时,SQL里最好加上条件判断,比如UPDATE product SET stock = stock - #{count} WHERE id = #{id} AND stock >= #{count},这样能避免超卖。如果后面要支持高并发,再引入Redis和乐观锁。
购物车表相对简单,核心字段是用户ID、商品ID、数量。这里建议加一个唯一索引,约束同一用户对同一商品只能有一条购物车记录,新增商品时用ON DUPLICATE KEY UPDATE或先查后改的方式,避免重复数据。
订单表和订单明细表需要重点设计。订单表存一次订单的汇总信息:订单编号、用户ID、总金额、订单状态、收货地址快照、创建时间。订单明细表存这个订单里每一个商品的快照:商品名称、单价、购买数量、小计。用户下单后如果商品改名、改价,历史订单里的快照不会受影响,这是电商系统的基本要求。
2.2 登录鉴权与接口权限设计
系统里的接口分为公开接口和登录接口两类。商品列表、商品详情这种浏览类接口可以公开,但购物车、订单提交、个人资料这些接口必须登录后才能访问。
常见的做法是JWT鉴权。用户登录成功后,后端签发一个token,前端存在localStorage或者Pinia里,每次请求时在请求头加上Authorization: Bearer token。后端写一个拦截器或过滤器,统一校验token,校验通过后把用户ID放到请求上下文中,业务层直接从上下文取当前用户。
管理员接口需要再校验一次角色。比如商品新增、订单发货这类接口,不仅要求登录,还要求当前用户角色是管理员。实现方式是在拦截器里解析token后,顺便把角色信息塞进请求上下文,然后在管理员接口上加权限校验注解或者手动判断。这套体系写起来不难,但权限边界一定要清晰,否则就会出现普通用户直接调接口删商品的情况。
2.3 购物车与订单流程的代码实现
购物车流程可以拆成四个接口:加入购物车、修改数量、删除商品、查询购物车列表。加入购物车时先判断该商品是否已存在,存在则数量加一,不存在则新增记录。前端购物车页面的数量加减按钮,每次改动后实时调用修改数量接口,这样即使页面刷新,数据也不会丢失。
订单流程相对复杂,建议按以下顺序实现:
- 用户从购物车勾选商品,点击结算
- 前端把选中的购物车记录ID列表传给后端
- 后端根据购物车ID查出商品和数量,计算总金额
- 校验商品库存是否充足
- 生成订单记录和订单明细记录
- 扣减库存
- 删除对应的购物车记录
- 返回订单编号和支付金额
这里有一个事务问题。第4到第7步必须放在一个数据库事务里,任何一步失败都要整体回滚。尤其是扣库存和删除购物车,不能出现订单创建成功但库存没扣、或者购物车商品还在的情况。Spring的@Transactional注解就是干这个的。
订单编号的生成也要注意,不能用简单的自增ID,建议使用日期时间加随机数的形式,比如yyyyMMddHHmmss加上六位随机数,保证可读性和唯一性。如果订单量很大,还可以引入雪花算法,但这类项目用时间戳加随机数已经完全够用。
2.4 前端页面与状态管理
前端部分,Vue3项目通常用Pinia做状态管理,主要存用户token、用户信息、购物车商品数量。登录成功后把用户信息写入Pinia,同时持久化到localStorage,刷新页面时再从localStorage恢复,避免登录状态丢失。
路由守卫是前端安全的一道基础防线。在路由配置里,给需要登录的页面加上meta.requiresAuth标记,然后在全局前置守卫里判断:没有token就跳转到登录页,并带上redirect参数,登录成功后可以跳回原页面。管理员端还可以再套一层admin路由,通过用户角色判断是否能访问,做到菜单层面和路由层面的双重控制。
接口请求层面,建议封装统一的axios实例。请求拦截器里自动携带token,响应拦截器里统一处理HTTP错误码和业务错误码。比如后台返回401说明token过期,前端就清除本地登录状态并跳回登录页。这种做法能让所有接口的错误处理逻辑保持一致,不用每个页面各自写一遍。
3. 从零启动项目的实操全流程
3.1 本地环境准备
先把环境装齐。后端需要JDK8或JDK11、Maven3.6以上;前端需要Node.js 16以上,npm或yarn;数据库需要MySQL 8.0,另外准备一个数据库客户端工具,Navicat或IDEA自带的Database工具都可以。命令行输入java -version、mvn -v、node -v、npm -v确认版本正常。
这里提醒几个版本细节。JDK版本不要直接用最新的JDK21跑SpringBoot 2.x项目,部分老版本依赖在JDK21下会因为模块化问题报错,老老实实用JDK8。Node版本也不要追求最高,Vite 4.x在Node 18和20下表现稳定,Node 22偶尔会遇到兼容性提示。版本不是越新越好,能和项目匹配才是关键。
3.2 导入SQL脚本与后端配置
把项目后端源码用IDEA打开后,第一件事不是急着启动,而是先把数据库准备好。用客户端工具连接本地MySQL,执行项目文档中提供的sql脚本。脚本一般会创建数据库和所有表,同时插入默认的管理员账号和几条测试商品数据。
然后修改后端配置文件application.yml。核心配置有四块:
server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/phone_mall?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false username: root password: 你自己的密码 driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.entity configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl配置文件改完之后,找到启动类,直接运行main方法。如果Maven依赖下载顺利,控制台出现类似Tomcat started on port 8080的日志,说明后端启动成功。此时可以先在浏览器访问一下接口地址,比如商品列表接口,能返回JSON数据就说明数据库连接和Mapper都没问题。
3.3 前端依赖安装与启动
后端起来之后,用终端进入前端目录,执行依赖安装:
npm install如果网络状况一般,可以指定镜像源加速:
npm install --registry=https://registry.npmmirror.com安装完成后启动开发服务器:
npm run dev正常会在终端看到Vite启动的本地地址,通常是http://localhost:5173。打开后如果页面能加载,但接口请求报跨域或404错误,那就需要检查前端代理配置。Vite工程里找到vite.config.js,配置大致如下:
server: { port: 5173, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } }配置完重启前端,接口就能正常请求了。此时用管理员账号登录后台,看一下商品管理、订单管理页面是否正常展示数据;再用普通用户注册一个账号,走一遍浏览商品、加入购物车、提交订单的完整流程。
3.4 数据初始化与演示账号
文档里一般会写清楚预置账号。管理员账号通常是admin,初始密码在SQL脚本里可以看到。普通用户账号需要自己在注册页注册。第一次启动成功后,建议先不急着改代码,把系统所有功能点都点一遍,记录哪些页面报错、哪些接口返回异常,再结合文档判断是环境问题还是代码问题。这一步看起来简单,但很多人忽略,后面改代码时容易越改越乱。
4. 常见问题与排查技巧实录
4.1 依赖下载慢或失败
Maven第一次加载SpringBoot项目依赖可能要下载几百MB的jar包,如果网络不好,经常卡在下载阶段。解决办法是修改Maven的settings.xml,配置镜像源。前端npm也一样,npm install报网络错误时先考虑镜像问题。
还有一类情况是npm install报ERESOLVE unable to resolve dependency tree,这通常是因为依赖版本冲突。解决办法是执行:
npm install --legacy-peer-deps这会让npm忽略peerDependency的严格校验,在二开老项目时非常实用。
4.2 MySQL连接失败与时区问题
MySQL 8.0和旧版驱动之间有不少坑。启动后端时报Failed to configure a DataSource或者Access denied,先检查用户名密码是否正确。如果报Public Key Retrieval is not allowed,需要在连接串上追加allowPublicKeyRetrieval=true。
另一个高频错误是serverTimezone时区异常。MySQL 8.0默认时区和Java默认时区不一致,会导致日期字段查询结果偏差几个小时。连接串里一定要带上serverTimezone=Asia/Shanghai,同时确保MySQL服务器时区与之一致。
如果在本地用命令行进入MySQL没问题,但Java连不上,多半是账号权限问题,执行一句授权SQL就能解决:
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED BY '密码'; FLUSH PRIVILEGES;4.3 前端接口请求跨域或401
开发环境跨域,优先检查Vite的proxy配置。很多人把proxy写在vite.config.js里,但没注意target需要指向后端实际端口,或者proxy配了但是接口请求地址没有以/api开头,导致代理没生效。这类问题建议先看浏览器Network面板,看看请求状态和响应头,再决定调前端代理还是后端CORS。
401错误代表token无效或缺失。排查思路是:登录是否返回了token、axios请求拦截器是否把token加到请求头、后端拦截器是否放行了登录接口。还有一点,token过期时间也不能设置得太短,否则用户浏览几分钟就被强制下线,体验很糟。
如果后端自己配置了CORS过滤器,而前端也配了代理,有时候会重复处理跨域,反而出现奇怪问题。建议开发环境统一走前端proxy,后端就不需要额外配置CORS;或者后端统一开启CORS,前端不用proxy,直连后端地址。两种方式选一种,避免双重处理。
4.4 分页查询不生效
MyBatis-Plus最让人困惑的问题之一就是分页。很多人写完分页查询,发现返回的total一直是0,或者查询结果把整表数据都返回了。原因基本是一个:没有配置分页拦截器。
在SpringBoot 2.x中,需要在配置类里注册MybatisPlusInterceptor,并添加PaginationInnerInterceptor。配置代码如下:
@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }配置完这个bean,Page对象的total和records才会正常工作。另外分页参数从前端传过来时,pageNum和pageSize要对应上,当前端传pageNum=1、pageSize=10,而代码里用的是current和size这两个字段名时,也会出现查不出数据的情况。
4.5 启动报端口被占用
8080端口被占是家常便饭。Windows下用命令查占用进程:
netstat -ano | findstr 8080找到PID后,在任务管理器里结束对应进程,或者直接把后端端口改成8081。注意改端口以后,前端proxy的target也要一起改,否则前后端就失联了。
5. 二次开发方向与上线部署建议
5.1 功能扩展方向
这套系统跑通之后,可以往几个方向做二次开发,优先级可以这样排:
第一,接入Redis。当前token校验每次都走数据库或内存,商品列表每次也都查库。引入Redis后,可以把登录token、商品详情热点数据、购物车数量缓存起来,性能提升非常明显。这也是很多面试官喜欢问的点。
第二,完善订单支付流程。当前下单后一般直接标记为待支付或直接置为待发货,如果想让流程更完整,可以对接支付沙箱,或者做一个模拟支付页面。支付回调后更新订单状态,这样整个电商闭环就完整了。
第三,增强搜索能力。手机销售站点的搜索可以按品牌、价格区间、内存大小做复合筛选。数据量少时,用MySQL的LIKE加条件查询就够了;数据量大了再考虑接入全文检索,但学习阶段没必要一开始就上重型组件。
第四,补充图片上传。商品主图和详情图片,最好能上传到云厂商的对象存储,而不是本地磁盘。本地存储的图片,部署到服务器后重启容易丢,也不方便多实例共享。接入对象存储SDK后,上传接口返回一个URL,前端直接展示。
5.2 生产环境打包部署
部署步骤其实很简单。后端在项目根目录执行:
mvn clean package -DskipTeststarget目录下会生成一个jar包,执行:
java -jar phone-mall.jar就能启动。前端先构建静态文件:
npm run builddist目录下的文件就是前端产物。生产环境建议用Nginx托管前端静态资源,同时反向代理后端接口。示例配置如下:
server { listen 80; server_name yourdomain.com; root /usr/share/nginx/html; index 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; } location / { try_files $uri $uri/ /index.html; } }这里的关键是location /里的try_files配置,因为Vue是单页应用,路由是前端控制的,刷新页面时必须让Nginx回退到index.html,否则会出现刷新后404的问题。
另外,生产环境上线前一定要改数据库密码、JWT密钥、管理员默认密码这三样东西。这些如果保持默认值,基本等同于把系统大门敞开。日志输出级别也可以从DEBUG调整为INFO,避免生产环境刷屏。
6. 关于这套源码的使用心得
最后说点个人实操中的体会。很多人拿到一套源码,第一反应是直接改代码,但我的建议是先把文档从头到尾读一遍,尤其看数据库脚本和接口说明。这套项目带的文档里通常已经把表关系、接口地址、部署步骤写清楚了,照着走一遍,能省掉大量瞎猜的时间。
启动过程中如果遇到环境问题,不要急着怀疑源码,九成以上是JDK、Node、MySQL版本不匹配,或者配置文件里的连接参数没改对。先确认环境,再怀疑代码,排查效率会高很多。
还有一点,这类前后端分离项目最适合用来练完整的开发流程:本地启动、联调、打包、部署、上线。哪怕只是把部署流程完整走一遍,你收获的东西也比单纯看教程多得多。技术栈里每个组件都不是孤立的,把它们串起来的体验,只有真正跑通一遍才能体会到。