ARTICLE DETAIL

资讯详情

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

Spring Boot网上购物商城后端源码:从跑通到二次开发全指南

Spring Boot网上购物商城后端源码:从跑通到二次开发全指南 简介本资源是一套基于Spring Boot框架的网上购物商城后端系统源码面向具备Java Web基础、希望学习或二次开发小型电商项目的开发者与在校学生。项目采用MyBatis Plus完成数据库操作围绕地址管理、购物车管理、客服管理、商品评论管理四大模块提供增删改查、分页与条件查询、详情查询、默认地址获取及提醒等接口可作为课程设计、毕业设计或练手项目的后端参考。压缩包共772个文件约14.62MB其中121个Java源文件承载核心业务逻辑153个JavaScript与46个Vue文件对应前端页面交互另有162个SVG、79个GIF及若干CSS、HTML等静态资源并包含SQL脚本、XML配置与批处理启动文件目录结构清晰便于按模块检索与运行调试。目前已有87人学习下载适合想快速理解Spring Boot电商后端分层设计与接口实现方式的读者参考借鉴。1. 拿到一份 Spring Boot 网上购物商城后端源码先别急着跑很多人拿到「基于 Spring Boot 框架的网上购物商城后端系统」这类源码压缩包第一反应是解压、找application.yml、改数据库密码、mvn spring-boot:run然后被一串Table xxx doesnt exist或者Access denied for user拍在脸上。我见过太多人卡在这一步最后把源码丢进回收站还留下一句「这玩意儿跑不起来」。其实问题不在代码在于没搞清楚这套后端系统到底由哪些模块拼起来、依赖什么中间件、启动顺序是什么。网上购物商城后端系统本质是一套围绕「用户—商品—购物车—订单—支付—库存」这条主链路展开的 REST 接口服务。它通常包含用户认证、商品管理、购物车、订单、支付回调、库存扣减、后台管理等模块技术栈以 Spring Boot MyBatis/MyBatis-Plus MySQL Redis 为主部分版本会引入 Spring Security 或 JWT 做鉴权。这类源码的价值不在于「能不能直接上线」而在于它把电商后端最核心的领域模型和接口分层完整地摆在你面前适合拿来改造成自己的项目、做课程设计、或者作为二次开发的起点。这篇文章面向三类人想拿这套源码跑通并读懂主链路的开发者、想基于它做二次开发或课程设计的学生、以及想借它理解电商后端分层设计的一线工程师。我会按「环境准备 → 数据库与配置 → 核心链路拆解 → 接口验证 → 避坑 → 进阶改造」的顺序讲每一步都给可复制的命令和参数说明让你不只是跑起来而是知道每一层在干什么。2. 环境准备与工程结构把依赖和目录先摸清楚2.1 运行这套商城后端需要哪些环境在动手之前先把环境对齐。这类 Spring Boot 商城源码的典型依赖如下表具体版本以你手上pom.xml为准不要照搬我这里的数字先打开pom.xml确认。组件常见版本区间作用是否必须JDK8 / 11 / 17编译运行必须Maven3.6依赖管理与构建必须MySQL5.7 / 8.0主业务数据存储必须Redis5.0缓存、购物车、验证码多数版本必须RabbitMQ / RocketMQ按需订单超时、异步通知部分版本可选Nginx任意静态资源与反向代理可选判断 JDK 版本最直接的办法是看pom.xml里的java.version或maven.compiler.source。如果是 Spring Boot 2.xJDK 8 或 11 都能跑如果是 Spring Boot 3.x最低 JDK 17而且javax.*包名会变成jakarta.*这一点在改代码时是血泪经验别问我怎么知道的。# 确认本机环境 java -version mvn -v mysql --version redis-server --version这三条命令输出正常再往下走。如果mvn -v报错先配MAVEN_HOME和PATH如果 MySQL 没装建议用 Docker 起一个省得污染本机环境。# 用 Docker 快速起 MySQL 和 Redis推荐避免版本冲突 docker run -d --name mall-mysql -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDroot123 \ -e MYSQL_DATABASEmall \ mysql:8.0 --default-authentication-pluginmysql_native_password docker run -d --name mall-redis -p 6379:6379 redis:6.2参数说明MYSQL_DATABASEmall会帮你建好库省去手动CREATE DATABASE--default-authentication-pluginmysql_native_password是为了兼容老版本 JDBC 驱动MySQL 8 默认的caching_sha2_password经常让老驱动连不上这是最常见的翻车点之一。Redis 用默认无密码配置生产环境一定要加requirepass本地调试图省事可以不加。2.2 工程目录怎么读先找入口和分层解压源码后先别急着看业务代码按这个顺序读目录# 查看工程骨架 tree -L 3 -I target|.git|.idea典型结构长这样mall-backend/ ├── pom.xml ├── src/main/java/com/xxx/mall/ │ ├── MallApplication.java # 启动类 │ ├── controller/ # 接口层 │ ├── service/ # 业务层 │ │ └── impl/ │ ├── mapper/ # 持久层接口 │ ├── entity/ # 数据库实体 │ ├── dto/ vo/ # 传输对象 │ ├── config/ # 配置类 │ └── common/ # 统一返回、异常、工具 └── src/main/resources/ ├── application.yml ├── mapper/ # MyBatis XML └── sql/ # 建表脚本读目录的目的是快速定位三样东西启动类在哪、配置文件在哪、SQL 脚本在哪。启动类一般在包根目录名字通常是XxxApplication配置文件在resources下可能是application.yml也可能是application-dev.yml多环境拆分SQL 脚本有的放在resources/sql有的干脆没给需要你自己根据实体类反推建表语句——后者是最坑的情况后面避坑章节会专门讲。提示如果resources下没有 SQL 脚本先看entity包里的实体类字段和注解再结合mapper里的 XML 查询语句反推表结构比盲目猜要快得多。3. 数据库与配置让项目真正连上你的环境3.1 导入建表脚本与初始化数据找到 SQL 脚本后先看它有没有CREATE DATABASE和USE语句。很多脚本只写了CREATE TABLE直接执行会报「No database selected」。# 方式一命令行导入 mysql -h127.0.0.1 -uroot -proot123 mall src/main/resources/sql/mall.sql # 方式二先登录再 source mysql -h127.0.0.1 -uroot -proot123 mysql CREATE DATABASE IF NOT EXISTS mall DEFAULT CHARSET utf8mb4; mysql USE mall; mysql SOURCE /绝对路径/src/main/resources/sql/mall.sql;导入完成后验证-- 检查表是否齐全 SHOW TABLES; -- 典型商城至少应有这些表 -- user, product, category, cart, orders, order_item, address, payment SELECT COUNT(*) FROM product;如果product表是空的说明脚本只建了表没插数据这时候前端页面会一片空白别以为是接口坏了。初始化数据一般在脚本后半段用INSERT INTO写的检查一下有没有被注释掉。3.2 application.yml 里必须改的四个地方打开配置文件重点看这几项spring: datasource: url: jdbc:mysql://localhost:3306/mall?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse username: root password: root123 driver-class-name: com.mysql.cj.jdbc.Driver redis: host: localhost port: 6379 database: 0 # password: 你的redis密码 server: port: 8080 servlet: context-path: /api四个必改点datasource.url里的库名和时区、username/password、redis.host/port、server.port。时区参数serverTimezoneAsia/Shanghai不加的话订单时间会差 8 小时这种玄学问题排查起来很费劲。context-path决定了你所有接口的前缀如果设了/api那访问路径就是http://localhost:8080/api/xxx别漏了。# 启动项目 mvn clean spring-boot:run # 或者打包后运行 mvn clean package -DskipTests java -jar target/mall-backend-1.0.0.jar看到Started MallApplication in x.xxx seconds就算启动成功。如果卡在HikariPool或者报Communications link failure八成是数据库连不上回头检查 URL、账号密码、MySQL 是否允许远程连接。3.3 用一条接口验证主链路是否通启动成功后别急着测业务先用一个最简单的查询接口确认整条链路Controller → Service → Mapper → DB是通的。# 查询商品列表路径以你实际 Controller 映射为准 curl -X GET http://localhost:8080/api/product/list?pageNum1pageSize10 \ -H Content-Type: application/json正常返回应该是统一格式的 JSON类似{ code: 200, message: success, data: { total: 20, list: [{id: 1, name: 测试商品, price: 99.00}] } }如果返回code: 500看控制台堆栈如果返回 404检查context-path和 Controller 的RequestMapping是否对得上如果返回空数据但code: 200说明链路通了只是表里没数据。这一步能过后面才有得玩。4. 核心链路拆解用户、购物车、订单三块怎么读4.1 用户认证JWT 还是 Session先看拦截器商城后端的鉴权方式主要有两种Session 拦截器或者 JWT 过滤器。判断方法很简单看config包里有没有WebMvcConfig注册拦截器或者有没有JwtFilter、TokenUtil这类类。// 典型 JWT 拦截器逻辑简化示意 public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (token null || !jwtUtil.validate(token)) { response.setStatus(401); return false; } // 把用户信息塞进 ThreadLocal供后续业务取用 UserContext.set(jwtUtil.parseUserId(token)); return true; } }逻辑说明拦截器在请求进入 Controller 之前校验 token校验通过就把用户 ID 放进ThreadLocalService 层通过UserContext.getUserId()拿到当前登录用户避免每个接口都手动解析 token。参数上Authorization头一般带Bearer前缀解析时要substring(7)去掉这个细节漏了会导致 token 永远校验失败。读这块代码时重点关注token 有效期设了多久、刷新机制有没有、密码是用 BCrypt 还是 MD5 存的。如果是 MD5 明文加盐说明这套源码的安全设计比较老二次开发时建议换成 BCrypt。4.2 购物车为什么多数实现选 Redis 而不是直接落库购物车是高频读写、低频持久化的典型场景。用户加购、改数量、勾选这些操作如果每次都写 MySQL数据库压力会很大。所以多数商城源码会把购物车放 Redis用 Hash 结构存key 是cart:{userId}field 是productIdvalue 是数量和勾选状态。// 加入购物车Redis Hash 实现 public void addToCart(Long userId, Long productId, Integer count) { String key cart: userId; // 先查商品是否存在、库存是否足够 Product product productMapper.selectById(productId); if (product null || product.getStock() count) { throw new BizException(商品不存在或库存不足); } // hashIncrement 支持数量累加避免并发覆盖 redisTemplate.opsForHash().increment(key, productId.toString(), count); // 设置过期时间防止冷用户购物车常驻内存 redisTemplate.expire(key, 7, TimeUnit.DAYS); }逻辑说明用increment而不是先get再put是为了避免并发下数量丢失。参数上过期时间设 7 天是常见折中太短用户回来发现购物车空了太长内存占用高。读这块要注意下单成功后购物车对应项有没有被清除、商品下架后购物车里的脏数据怎么处理这两处是很多源码的薄弱点。4.3 订单创建库存扣减和超时取消是重灾区订单链路是整个商城后端最复杂的地方核心就两件事下单时怎么扣库存、下单后没付款怎么取消。Transactional(rollbackFor Exception.class) public Long createOrder(Long userId, ListCartItem items) { // 1. 校验库存并扣减乐观锁带 stock 条件更新 for (CartItem item : items) { int affected productMapper.reduceStock(item.getProductId(), item.getCount()); if (affected 0) { throw new BizException(库存不足 item.getProductId()); } } // 2. 生成订单主表和明细 Order order buildOrder(userId, items); orderMapper.insert(order); // 3. 发送延迟消息30 分钟未支付则取消 mqTemplate.sendDelay(order.cancel, order.getId(), 30 * 60 * 1000); return order.getId(); }对应的库存扣减 SQL 必须带条件这是防超卖的关键UPDATE product SET stock stock - #{count} WHERE id #{productId} AND stock #{count};逻辑说明WHERE stock #{count}保证库存不足时更新影响行数为 0Service 层据此抛异常回滚。如果写成先SELECT再UPDATE并发下必然超卖。参数上延迟消息的时间要和业务约定一致30 分钟是电商常见值测试时可以改成 1 分钟方便验证。如果源码里没有 MQ通常会用定时任务扫orders表里status待支付 AND create_time now-30min的记录来取消效果一样但实时性差一些。5. 避坑与排查跑不起来时先看这几条5.1 启动报 Table doesnt exist现象启动日志或首次请求时报Table mall.xxx doesnt exist。 原因SQL 脚本没导入、导入到了错误的库、或者表名大小写不一致Linux 下 MySQL 默认区分大小写Windows 不区分。 解决SHOW TABLES确认表在不在当前库检查application.yml里的库名和导入时用的库名是否一致如果是大小写问题在my.cnf里加lower_case_table_names1后重启 MySQL或者统一实体类TableName和实际表名的大小写。5.2 接口返回 401 但明明登录了现象登录接口能拿到 token但调其他接口一直 401。 原因token 没放进请求头、请求头名字写错、或者 token 前缀Bearer没带。 解决确认请求头是Authorization: Bearer xxxxx注意Bearer后面有一个空格检查拦截器里解析 token 时有没有substring(7)如果用的是 Postman确认没把 token 填到 Params 而不是 Headers。5.3 订单时间差 8 小时现象下单后数据库里create_time比实际时间早或晚 8 小时。 原因JDBC URL 没设时区或者 MySQL 服务端时区和 JVM 时区不一致。 解决URL 加serverTimezoneAsia/Shanghai实体类时间字段用LocalDateTime而不是java.util.Date如果还不对检查 MySQL 的SELECT global.time_zone, session.time_zone;必要时在配置文件里统一。5.4 Redis 连不上导致整个项目起不来现象启动时报Unable to connect to Redis但业务其实不依赖 Redis。 原因spring-boot-starter-data-redis引入后健康检查会强制连 Redis连不上就启动失败。 解决本地调试先确保 Redis 起着如果确实不想用 Redis把相关依赖和RedisConfig排除掉或者把management.health.redis.enabled设为false。生产环境则要配好连接池和超时别用默认值。5.5 前端跨域请求被拦现象浏览器控制台报CORS policy错误接口用 curl 却能通。 原因后端没配跨域或者配了但没生效。 解决在config包里加全局跨域配置允许前端域名、方法和请求头如果用 Nginx 代理也可以在 Nginx 层加add_header Access-Control-Allow-Origin。注意allowCredentials(true)时allowedOrigins不能用*这是常见翻车点。Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) // 允许凭证时用 pattern 而非 origin .allowedMethods(GET, POST, PUT, DELETE) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }6. 二次开发与验证把源码变成你自己的项目跑通只是起点真正有价值的是把它改成能用的东西。我一般会按这个顺序做二次开发先补测试再改鉴权最后接真实支付。补测试是最容易被跳过但收益最高的一步。用SpringBootTest给订单创建写一个集成测试覆盖「库存充足下单成功」和「库存不足下单失败」两个用例跑通之后你改任何代码都有底。SpringBootTest class OrderServiceTest { Autowired private OrderService orderService; Autowired private ProductMapper productMapper; Test void createOrder_whenStockEnough_shouldSuccess() { // 准备插入一个库存为 10 的商品 Product p new Product(); p.setName(测试商品); p.setStock(10); productMapper.insert(p); // 执行下单 2 件 Long orderId orderService.createOrder(1L, List.of(new CartItem(p.getId(), 2))); // 断言订单生成且库存扣到 8 assertNotNull(orderId); assertEquals(8, productMapper.selectById(p.getId()).getStock()); } }参数说明SpringBootTest会加载完整上下文测试库建议单独配一个application-test.yml避免污染开发数据。断言里直接查库验证库存比只看返回值可靠。改鉴权是第二个重点。如果原源码用 Session改成 JWT 后要同步改拦截器、登录接口和前端存储方式如果已经是 JWT重点看 token 过期时间和刷新逻辑把有效期从默认的 24 小时改成业务需要的值并加上刷新接口。接真实支付是最后一步也是最需要谨慎的一步。多数源码里的支付是模拟的只改订单状态。接真实支付时核心是「异步回调 签名验证 幂等处理」三件事回调接口要验签、要防重复通知、要在事务里更新订单状态。这三件事任何一件漏了都可能造成重复发货或订单状态错乱。验证方法上我习惯用「接口 数据库 日志」三对照调一个下单接口同时看返回 JSON、查orders和product表、翻应用日志里的 SQL 输出。三者一致才算真正跑通。如果只信接口返回很容易被「返回成功但实际没落库」骗过去。最后说个我自己的习惯拿到任何一份商城源码我都会先画一张主链路时序图把「用户请求 → 拦截器 → Controller → Service → Mapper → DB/Redis」这条线走一遍标出每一步的输入输出和异常分支。图画完代码基本就懂了改起来也不慌。这套方法比逐行读代码快得多希望帮到你。本文还有配套的精品资源点击获取
返回列表