
简介这是一份基于Spring Boot与MySQL的游戏周边交易商城系统完整项目源码面向Java学习者及有毕业设计、课程设计需求的学生。系统覆盖用户注册登录、商品发布管理、购物车操作、订单生成与支付流转、管理员权限控制等核心电商模块功能链路完整可直接运行演示。压缩包共632个文件大小仅16.37MB包含89个Java后端源码、116个Vue前端页面、48个HTML、31个JS脚本、43个JAR依赖、30个XML配置及1个SQL数据库脚本等前后端代码与依赖划分清晰。内置.bat环境脚本覆盖Maven配置、镜像源设置、一键安装与无源运行等场景有效降低部署门槛。已有173人学习下载。项目涉及JWT身份验证、第三方支付集成、数据库表设计等实际开发要点。适合以此为主线逐模块研读理解Web应用完整开发流程并在此基础上扩展出属于自己的商城系统。1. 游戏周边交易商城基于Spring Boot MySQL的完整Java Web项目应该怎么往下改如果你搜到这份基于Spring Boot MySQL实现的游戏周边交易商城系统资源大概率是准备交毕业设计或者课程设计。这个项目本身不复杂它解决的是“从零搭一个能演示的Java商城”这件事用户登录注册、商品分类浏览、购物车、订单流程记录、后台商品管理这些模块它都有MySQL负责把数据落库Spring Boot负责把前后端完整串起来跑通。它适合三类人没系统做过Web项目的在校生、需要快速出一个能答辩的演示系统的毕业生、以及想在本地跑一套商城代码再二次开发的工程师。拿到手别急着点启动先按后面几步把模块边界、表结构和订单主链路过一遍。2. 系统模块与技术选型先看清单体结构再动手2.1 为什么毕业设计普遍用单体而不用微服务游戏周边商城这类项目数据是核心交易流程是主线。把它拆成微服务在毕设阶段没有收益反而会引入服务间调用、消息队列、分布式事务这些毕设根本用不上的复杂度。单体应用把所有模块放进同一个工程确实有短板并发能力上限低模块强耦合。但在课程设计和毕业设计这个阶段单体换来的是最直接的收益——部署一个jar包就能跑调试时一个进程就能跟踪完整链路订单写入、库存扣减这类操作可以直接用本地事务包住不需要考虑跨服务的数据一致性问题。很多答辩老师会问一个问题为什么不做微服务你不需要回避直接把取舍讲清楚就行。单体在数据一致性上的实现复杂度最低事务的原子性靠数据库和Spring就能保证将来要扩展用户、商品、订单、后台管理本来就可以按这四个边界打散成独立服务。这么回答说明你理解架构取舍而不是只会套框架。页面方案上也容易看出项目的风格。解压以后先看resources目录下有没有templates文件夹有templates文件夹且html文件里有th:标签就是Thymeleaf模板渲染没有templates而前端页面放在static目录里通过ajax请求后端接口这就是前后端分离的雏形。后者对前端调试更友好也更容易在答辩时演示接口调用过程。2.2 包结构分层与接口清单解压工程后第一步不是看代码而是看包结构。这类项目通常按controller、service、mapper、entity/pojo、dto、vo、config、interceptor这几个包来组织。一个标准的分层职责如下表包或层职责典型类名controller接收请求、参数校验、返回结果ItemControllerservice业务逻辑、事务边界ItemService / ItemServiceImplmapperSQL与数据库交互ItemMapperentity与数据表对应的实体Itemdto接收外部参数的传输对象OrderCreateDTOvo返回给前端的结果对象ItemVO / CartVOinterceptor登录校验等横切逻辑LoginInterceptorconfig全局配置、WebMvc配置WebMvcConfig看代码之前先把resources目录下的sql脚本和application配置找到。一个可运行的商城工程resources里必须有application.yml或application.properties里面配了数据源、端口、MyBatis相关配置。把这些拿到手以后我把整个项目先扫成一张接口清单登录接口、分类接口、商品列表接口、商品详情接口、加入购物车接口、下单接口、后台增删改接口。后面所有调试和答辩演示都围绕这张清单来不会跑偏。回到代码本身大多数这种商城工程会有一个统一的Result对象包装返回结果结构一般是code、msg、data三个字段。code200表示成功code500表示服务端异常code401表示未登录。前端页面拿到返回值以后先判断code再决定渲染数据还是弹错误提示。你后续改接口时保持这个三段式结构不变前端代码就基本不用动。2.3 环境版本JDK、Spring Boot、MySQL的最小可行性组合市面上流通的毕业设计资源Spring Boot 2.x配JDK 8最常见Spring Boot 3.x配JDK 17是另一套组合。两个版本之间最大的差别不只是写法而是javax包名和jakarta包名不兼容。如果你的工程是Spring Boot 2.3.x本机却装了JDK 17启动时大概率会在依赖加载阶段报错。MySQL的版本影响更直接。8.0之后的连接驱动、认证插件、时区处理方式和5.7差别明显最容易踩的是时区问题。连接串里不写serverTimezone会报“Server returns invalid timezone”之类的错误。下面是我处理这类资源时推荐的最小版本组合环境建议版本注意事项JDK1.8或17取决于pom.xml先看pom里java.version再装环境Maven3.6建议配阿里云镜像加速依赖下载Spring Boot2.3.x / 2.6.x 或匹配的3.x注意javax/jakarta包名差异MySQL5.7和8.0均可连接串必须带serverTimezone数据库客户端Navicat或MySQL Workbench导入sql脚本用pom.xml里还有一个让新手容易卡的坑Maven从中央仓库拉依赖在国内经常超时。解压以后第一件事是检查IDEA的Maven设置User settings file指向的settings.xml里有没有配阿里云镜像。不配镜像的话项目依赖加起来几十上百MB会一直卡在download。我处理这类资源时的习惯是先把settings.xml配好初始化完依赖再去看代码。3. 数据库设计与建表先看清表和字段再改需求3.1 商城核心表的职责边界与关系游戏周边商城的核心数据模型由六张表支撑用户表、商品类目表、商品表、购物车表、订单表、订单项表再加一张收货地址表也不少见。表之间的关系很简单类目对商品是一对多用户对订单是一对多订单对订单项是一对多用户对购物车记录是一对多。需要特别强调的是订单和订单项为什么要拆成两张表。很多新手建表时习惯把商品名、商品价格、购买数量直接塞进订单表的一个字段里这是一个典型的翻车设计。商品会改价、会下架、会改变描述订单一旦生成订单里的商品名称和成交价格就必须永久定格。所以订单项表里要冗余存商品快照信息——商品ID、商品名、商品图片、成交单价、购买数量。查询历史订单时只查订单项表不再去join商品表这样商品表未来怎么改都不影响历史数据。3.2 建表SQL从user到order_item的落库写法sql脚本通常放在resources目录或项目根目录的database文件夹下。下面这套建表逻辑覆盖了商城最常见的需求SQL直接兼容MySQL 5.7和8.0CREATE DATABASE IF NOT EXISTS game_mall DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci; USE game_mall; -- 用户表 CREATE TABLE user ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 用户ID, username VARCHAR(50) NOT NULL COMMENT 登录名, password VARCHAR(100) NOT NULL COMMENT BCrypt加密后的密码, nickname VARCHAR(50) DEFAULT NULL COMMENT 昵称, phone VARCHAR(20) DEFAULT NULL COMMENT 手机号, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; -- 商品类目表 CREATE TABLE category ( id BIGINT NOT NULL AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT 类目名称, parent_id BIGINT NOT NULL DEFAULT 0 COMMENT 父级ID0表示顶级, sort_order INT NOT NULL DEFAULT 0 COMMENT 排序值, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品类目表; -- 商品表 CREATE TABLE item ( id BIGINT NOT NULL AUTO_INCREMENT, category_id BIGINT NOT NULL COMMENT 所属类目, name VARCHAR(200) NOT NULL COMMENT 商品名称, subtitle VARCHAR(500) DEFAULT NULL COMMENT 副标题, main_image VARCHAR(500) DEFAULT NULL COMMENT 主图地址, price DECIMAL(10,2) NOT NULL COMMENT 当前售价, stock INT NOT NULL DEFAULT 0 COMMENT 库存数量, status TINYINT NOT NULL DEFAULT 1 COMMENT 1上架 0下架, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_category (category_id), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品表; -- 购物车表 CREATE TABLE cart ( id BIGINT NOT NULL AUTO_INCREMENT, user_id BIGINT NOT NULL COMMENT 用户ID, item_id BIGINT NOT NULL COMMENT 商品ID, quantity INT NOT NULL DEFAULT 1 COMMENT 数量, PRIMARY KEY (id), UNIQUE KEY uk_user_item (user_id, item_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT购物车表; -- 订单表 CREATE TABLE orders ( id BIGINT NOT NULL AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL COMMENT 业务订单号, user_id BIGINT NOT NULL COMMENT 下单用户, total_price DECIMAL(10,2) NOT NULL COMMENT 订单总价, receiver_name VARCHAR(50) DEFAULT NULL COMMENT 收货人快照, receiver_phone VARCHAR(20) DEFAULT NULL COMMENT 收货电话快照, receiver_address VARCHAR(200) DEFAULT NULL COMMENT 收货地址快照, status TINYINT NOT NULL DEFAULT 0 COMMENT 0待付款 1已付款 2已发货 3已完成 4已取消, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_user (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单表; -- 订单项表 CREATE TABLE order_item ( id BIGINT NOT NULL AUTO_INCREMENT, order_id BIGINT NOT NULL COMMENT 所属订单, item_id BIGINT NOT NULL COMMENT 商品ID仅作关联, item_name VARCHAR(200) NOT NULL COMMENT 商品名称快照, item_image VARCHAR(500) DEFAULT NULL COMMENT 商品图片快照, item_price DECIMAL(10,2) NOT NULL COMMENT 成交单价快照, quantity INT NOT NULL COMMENT 购买数量, PRIMARY KEY (id), KEY idx_order (order_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单项表;上面这套SQL有几个值得注意的点。第一金额字段全部用DECIMAL(10,2)商品价格和订单价格都不能用FLOAT或DOUBLE二进制浮点数存在精度误差账目算不齐在答辩时是致命伤。第二订单表没有直接叫order因为order是MySQL的保留字直接建表会被语法校验卡住。第三订单表里冗余了receiver_name、receiver_phone、receiver_address这一组收货地址快照下单那一刻地址就定格用户后面改地址不影响已有订单。第四购物车表用user_id和item_id建了唯一索引同一个用户重复加购同一商品时直接走索引冲突在Java代码里捕获DuplicateKeyException然后走数量累加比先查再更新更稳。3.3 字符集、事务隔离级别和索引选择数据库连接串里字符集参数要写在URL里。推荐写法是加上useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai多花几秒钟把参数写全能避开大部分乱码和时区故障。订单和库存的并发更新涉及MySQL事务处理默认隔离级别是REPEATABLE READ在课程设计这个量级下完全够用不建议为了显得专业去改隔离级别改动之后反而会在不同MySQL版本上出现行锁和间隙锁的行为差异。索引设计上外键字段和查询条件字段建索引就够了不要每列都加。入库数据量连十万都不到的课程项目三到五个索引已经是极限索引太多反而拖慢写入。4. 核心流程实现登录、下单和库存扣减这条主链路4.1 登录状态管理Session或JWT的取舍游戏周边商城的前端页面通常和Spring Boot工程放在一起同一个域名下访问这种场景用Session管理登录状态是最省事的。需求方要的是能跑通、能演示Session方案不需要额外依赖也不需要考虑Token过期刷新的问题。具体实现是登录成功后把userId放进HttpSession然后注册一个拦截器拦截所有非登录可访问的路径。public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); Object userId session.getAttribute(userId); if (userId null) { response.setContentType(application/json;charsetutf-8); response.getWriter().write({\code\:401,\msg\:\未登录\}); return false; } return true; } }拦截器逻辑很简单从Session里拿不到userId就说明没登录直接返回401放行则继续走Controller。需要在WebMvcConfig里注册拦截器并配置排除放行的路径通常是登录接口、注册接口、商品列表和商品详情。支付回调接口也必须放行否则第三方回调进不来。Configuration public class WebMvcConfig implements WebMvcConfigurer { Autowired private LoginInterceptor loginInterceptor; Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) .addPathPatterns(/**) .excludePathPatterns(/user/login, /user/register, /item/**, /category/**); } }注意excludePathPatterns里的路径要和你Controller里的RequestMapping完全对应。有的项目接口写的是/api/user/login有的写的是/user/login拦截器排除路径写错一个页面就会一直跳登录。如果你拿到的资源是前后端分离项目前端独立部署在Vue服务上跨域场景Session方案就得改成JWT Token。改动也不复杂登录成功后用userId生成Token返回给前端前端每次请求在Header里带Authorization字段后端用拦截器去解析校验Token。两种方式二选一判断标准就看前端页面是Spring Boot模板渲染出来的还是独立前端工程。4.2 商品分页列表接口的参数设计商品列表和后台管理列表都离不开分页。Controller接收pageNum和pageSize两个参数pageNum从1开始pageSize一般限定在10到50之间。RestController RequestMapping(/item) public class ItemController { Autowired private ItemService itemService; GetMapping(/list) public ResultPageResultItemVO list(RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize, RequestParam(required false) Long categoryId) { PageResultItemVO page itemService.pageItems(pageNum, pageSize, categoryId); return Result.success(page); } }Service层用MyBatis Plus的Page对象作为分页载体设置当前页和每页条数最后把entity列表转换成ItemVO返回。转换这一步不要省entity里如果有库存字段直接返回给前端等于把运营数据和展示数据混在一起了。Override public PageResultItemVO pageItems(Integer pageNum, Integer pageSize, Long categoryId) { LambdaQueryWrapperItem wrapper new LambdaQueryWrapper(); wrapper.eq(Item::getStatus, 1); if (categoryId ! null) { wrapper.eq(Item::getCategoryId, categoryId); } wrapper.orderByDesc(Item::getCreateTime); PageItem page new Page(pageNum, pageSize); PageItem result itemMapper.selectPage(page, wrapper); PageResultItemVO pageResult new PageResult(); pageResult.setTotal(result.getTotal()); pageResult.setList(result.getRecords().stream().map(this::toVO).collect(Collectors.toList())); return pageResult; }参数说明里有个容易被忽略的细节pageNum和pageSize都要做兜底处理。前端传一个pageNum999、pageSize9999进来SQL层面虽然不会报错但查询性能会很难看。后端校验逻辑一般把pageSize限制在100以内超过就按100处理。这是一个很小的防御性编程答辩时讲出来能加分。4.3 下单扣库存事务边界和SQL原子性订单模块是整个商城的核心。下单操作要做的事包括校验商品状态、校验库存、计算总价、插入订单表、插入订单项、扣减库存这几个步骤必须放在一个Service方法里加上Transactional注解。为什么不拆开订单表和库存表的数据一致性要求极高一旦订单表插入成功但库存扣减失败系统里会出现超卖或者数据对不上的问题。扣库存这里有一个常见误用。不少初学写法是先查询库存Java代码里判断if (stock 0)再执行update这在单线程下没问题并发一高就会出现两个请求同时读到stock1然后都通过判断去下单最终库存变成负数。正确做法是把判断条件放进SQL语句本身UPDATE item SET stock stock - #{quantity}, update_time NOW() WHERE id #{itemId} AND stock #{quantity}返回的int值就是受影响行数1表示扣减成功0表示库存不足。UPDATE操作在MySQL里是行级原子性的不需要额外加锁也避免了先查后改带来的并发窗口。Service层的核心逻辑如下Transactional(rollbackFor Exception.class) public OrderVO createOrder(OrderCreateDTO dto) { // 1. 校验商品状态 Item item itemMapper.selectById(dto.getItemId()); if (item null || item.getStatus() ! 1) { throw new BizException(商品不存在或已下架); } // 2. 扣减库存SQL行级原子性防超卖 int rows itemMapper.deductStock(dto.getItemId(), dto.getQuantity()); if (rows ! 1) { throw new BizException(库存不足); } // 3. 生成订单并保存 Orders order new Orders(); order.setOrderNo(generateOrderNo()); order.setUserId(dto.getUserId()); order.setTotalPrice(item.getPrice().multiply(new BigDecimal(dto.getQuantity()))); order.setStatus(0); orderMapper.insert(order); // 4. 保存订单项快照 OrderItem orderItem new OrderItem(); orderItem.setOrderId(order.getId()); orderItem.setItemId(item.getId()); orderItem.setItemName(item.getName()); orderItem.setItemImage(item.getMainImage()); orderItem.setItemPrice(item.getPrice()); orderItem.setQuantity(dto.getQuantity()); orderItemMapper.insert(orderItem); return orderVO; }这段代码有两点值得说明。第一Transactional注解的rollbackFor必须写成Exception.class只写Transactional不指定回滚策略时Spring默认只在运行时异常时回滚受检异常抛出来事务不会回滚。第二下单总价不能信任前端传进来的值要以服务端查询到的商品价格实时计算。前端传参只传商品ID和数量价格以数据库里的为准否则用户改一下请求体就能低价下单。提示如果下单接口在并发压测下频繁报库存不足先检查是不是库存判断写进了UPDATE语句本身。用了先查再改的写法并发场景一定会出问题。生成订单号这一块最简单的可用方案是时间戳加随机数比如yyyyMMddHHmmss加四位随机数字。课程项目不需要引入分布式ID中间件一个订单号兼顾可读性和唯一性就够。唯一索引uk_order_no兜底万一随机数碰撞第一次插入会抛异常重试一次就能过去。5. 部署与避坑从零跑到通的五个常见问题记录5.1 MySQL连接报错SSL、时区和驱动三者没对齐现象点启动后控制台报Communications link failure或者直接看到Caused by: java.sql.SQLException: The server time zone value Öйú±ê׼ʱ¼ä is unrecognized。原因MySQL 8.x的驱动对时区要求严格而jdbc连接串里没写serverTimezone同时用了MySQL 8默认的SSL认证。解决把数据源配置改成下面这种完整写法spring: datasource: url: jdbc:mysql://localhost:3306/game_mall?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.DriverallowPublicKeyRetrievaltrue这个参数很重要MySQL 8.0默认使用caching_sha2_password认证走公钥检索时需要这个开关不写就可能出现Access denied for user的诡异报错。driver-class-name最好显式写com.mysql.cj.jdbc.Driver不要写旧的com.mysql.jdbc.Driver。提示如果本机MySQL还是5.7driver-class-name同样推荐换成cj驱动MySQL 8的驱动能向下兼容连接5.7的库两边都能跑通。5.2 SQL脚本导入不完整导致表缺失现象项目能启动但注册或登录时后端报SQLSyntaxErrorException: Table game_mall.user doesnt exist。原因sql脚本没完整导入或者脚本本身没有CREATE DATABASE这一段数据导入到了别的数据库里。解决用Navicat或MySQL Workbench打开sql脚本整体执行一遍执行前检查脚本开头有没有USE game_mall;有这条才会落到正确库。执行完以后在客户端里刷一遍表列表把表数量和设计文档对一下。有的资源在sql脚本文件里还会放几行测试数据商品表里没数据登录进去看到空列表也是同样的排查路径。5.3 数据库连接池报错连接不够或连接被断现象并发场景下后台报Cannot get a connection, pool exhausted或者请求中途出现连接超时。原因默认配置里连接池太小而且MySQL服务端wait_timeout默认会把长时间空闲的连接断掉业务层拿到的却是已经失效的连接。解决在数据源配置里增加连接池参数idle-timeout要略小于MySQL的wait_timeout。参考配置如下spring: datasource: hikari: maximum-pool-size: 20 minimum-idle: 5 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000这组参数的意思是最少保持5个连接最多20个连接超时30秒最长生命周期30分钟。max-lifetime一定不能大于MySQL的wait_timeout否则连接池里的连接会被服务端清掉请求时拿到的全是死连接。用Druid的项目也有类似参数核心思路一样连接存活时间必须比数据库服务端的断开阈值短。5.4 中文乱码库、表、连接三层都要对齐utf8mb4现象商品名称、订单地址在数据库里正常页面上显示一串问号或者后台写入的数据直接变成???。原因数据库表字段虽然是utf8但连接串里没有带characterEncodingutf8或者前端页面本身是GBK编码。解决最省事的方法是统一成utf8mb4。先修改建库语句为DEFAULT CHARSET utf8mb4再在jdbc连接串里加上useUnicodetruecharacterEncodingutf8。MySQL 5.7环境里如果字符集修改不彻底可以执行ALTER TABLE item CONVERT TO CHARACTER SET utf8mb4;把存量表转过去。处理完以后记得重启应用再验证连接池里缓存的旧连接可能还带着旧的字符集设置。5.5 启动端口被占用和依赖冲突现象启动到一半报Web server failed to start. Port 8080 was already in use。原因本机已有进程占了默认端口最常见的是之前启动过的Spring Boot进程没杀掉或者别的开发工具占了8080。解决先把占用进程找出来杀掉或者直接在application.yml里改端口改成8081、8090都行。改完一定要同步检查前端页面的请求地址有没有写死8080。依赖冲突的表现是NoSuchMethodError、ClassNotFoundException原因多是pom里引入了重复版本的依赖解决办法是在IDEA右侧Maven面板里看Dependency Hierarchy找到冲突的依赖后用exclusion排除一个。这一层如果你用的是IntelliJ IDEA社区版跑项目它是没有Spring官方插件和Spring Initializr界面的但不影响打开和调试Spring Boot工程。社区版装好Lombok插件更新一下Maven依赖启动类上右键就能run跟收费版在跑Java工程上没差别。6. 进阶给商城加一个健康检查和连接池调优6.1 健康检查接口用最少的代码让项目具备可运维性答辩时只讲CRUD很容易被问住加一个健康检查接口是性价比很高的增量内容。不需要引入监控平台先做一个返回当前运行状态和数据库状态的小接口就够。RestController RequestMapping(/monitor) public class MonitorController { Autowired private DataSource dataSource; GetMapping(/health) public MapString, Object health() { MapString, Object result new HashMap(); result.put(status, UP); try (Connection conn dataSource.getConnection()) { result.put(db, UP); result.put(dbUrl, conn.getMetaData().getURL()); } catch (SQLException e) { result.put(db, DOWN); result.put(error, e.getMessage()); } return result; } }实现很直白从Spring容器里拿出DataSource尝试拿一个数据库连接拿到就说明MySQL还活着。将来想再进一步可以在这个基础上引入Spring Boot Actuator或Spring Boot Admin暴露/actuator/health、/actuator/info这些标准端点。对毕业设计而言自定义这个Monitor接口已经足够展示你在可维护性上的考虑。6.2 连接池参数与事务验证数据库连接池不是越大越好也不是默认配置就能扛住订单峰值。以HikariCP为例经验值是minimum-idle5maximum-pool-size20当并发请求量超过20时请求会排队等待空闲连接这正好保护了下游数据库不被瞬时流量压垮。这组参数要和事务配合验证开两个浏览器对同一件只剩1件库存的商品同时下单正常情况应该只有一个订单成功另一个拿到“库存不足”。如果两次都成功且库存为负就是前面4.3节讲的扣库存SQL写成了先查再改。我一般拿到这类资源解压以后有个习惯是强制走一遍“注册新用户 → 浏览商品 → 加购物车 → 下单 → 后台改库存”这条完整链路每个环节看一眼数据库对应表是否更新。任何环节出问题排查路径都是先看后端接口日志再看事务有没有回滚最后查数据库当前状态。从那以后我每次拿到传过来的项目第一件事就是打开资源配置文件把端口、数据源和依赖树过一遍确认没问题再启动。希望帮到你。本文还有配套的精品资源点击获取