SpringBoot3+Vue3足球观赛售卖系统全栈实战项目解析 📅 发布时间:2026/9/6 7:39:52 👁 浏览次数: 如果你正在找 Java 课程设计、毕业设计选题或者想做一个“带真实业务逻辑、不只有基础 CRUD”的 SpringBoot3 Vue3 全栈项目那这个“足球观赛售卖系统”可以直接拿来做参考。这次我们来看一个典型的 Java 全栈管理系统后端用 SpringBoot3前端用 Vue.js3 Element Plus数据库用 MySQL。业务范围覆盖用户登录、赛事场次管理、看台座位选择、球票/纪念品售卖、订单支付流程、优惠券、数据统计看板等功能模块。它比单表的增删改查更有价值能覆盖权限、事务、定时任务、图表报表、微信支付回调或测试沙箱支付等真实业务场景。本文会以“从零搭建 跑通流程 验证效果”的顺序展开先是核心能力速览和环境准备再给项目目录结构与数据库表设计接着是后端接口编写、前端页面联调最后是启动测试、接口验证、常见问题排查和部署上线注意点。如果你现在做毕业设计、实训项目或者想提高 SpringBoot3 Vue3 的工程化能力这篇文章可以直接收藏。1. 核心能力速览能力项说明项目类型前后端分离的 Java Web 管理系统后端技术栈Java SpringBoot3 Spring MVC MyBatis-Plus MySQL前端技术栈Vue.js3 Vite Element Plus Pinia Vue Router Axios数据库MySQL 8.x用户角色普通用户 / 系统管理员建议用 Spring Security JWT 做权限控制核心业务赛事场次、看台与座位、球票售卖、纪念品售卖、购物车、订单、支付回调、优惠券、数据统计启动方式后端 Maven 启动 前端 npm 启动是否支持接口 API支持RESTful 风格可提供给小程序或 APP 复用是否支持批量任务支持可封装批量导入赛事场次、批量释放过期订单座位适合场景Java 课程设计、毕业设计、小型体育赛事售货商城系统原型从技术角度看这个项目的核心不是复杂算法而是把“商品球票和纪念品→ 购物车 → 订单 → 支付 → 库存扣减 → 订单统计”这条链路完整打通。前端能用 Vue3 组件化方式展示看台、赛事列表和订单中心后端能用 SpringBoot3 统一处理业务异常、事务回滚和权限校验。2. 适用场景与业务边界2.1 适合什么场景Java 课程设计、毕业设计功能复杂度足够前后端技术栈新演示效果好。小型体育赛事线上售卖原型可以快速扩展成真实赛事售票系统。SpringBoot3 Vue3 全栈学习项目能覆盖主流开发流程、接口联调、权限管理。前后端分离架构实践后端只提供 API前端独立构建。2.2 能解决什么问题系统要解决的核心问题包括赛事和场次信息杂乱、球票售卖状态不透明、座位选择困难、订单与库存不同步、销售数据统计费时。最终交付的成果应该是管理员可以维护赛事、场次、看台、票价档位、纪念品库存用户在页面上能看赛事列表、选场次、选看台座位、加购物车、下单支付支付成功回写订单状态并锁定座位未支付订单超时自动释放后台用图表展示销售额、出票量、商品销量。2.3 不适合什么场景高并发抢票该系统适合中小规模售票。真正的大规模抢票需要消息队列、分布式锁、限流、分库分表已经超出课程设计边界。真实在线支付如果接入真实微信支付/支付宝需要企业资质和正式商户号。个人学习建议使用支付沙箱或模拟支付。超大型前端后台如果要做多级组织架构、复杂的供应链管理选型上应该考虑更完整的企业级脚手架。2.4 版权、隐私与安全边界无论做什么售卖系统都要注意几个点赛事信息、球队 Logo、球星照片涉及版权演示环境不能直接使用未经授权的商业素材用户手机号、收货地址属于个人敏感信息数据库不能明文存储前端不能过度采集涉及线上支付时必须走官方支付渠道或测试沙箱不能私自伪造支付回调系统上线前要做基础安全加固比如密码加密、SQL 注入防护、接口鉴权。3. 环境准备与前置条件3.1 版本清单建议用以下版本组合兼容性较好组件版本建议说明JDKJDK 17 或 JDK 21SpringBoot3 要求 JDK 17 起步Maven3.8项目构建和依赖管理Node.js18.x 或 20.x LTSVue3 Vite 开发环境npm/pnpmnpm 9 / pnpm 8前端依赖安装MySQLMySQL 8.x建议用 8.0 以上版本IDEIntelliJ IDEA / VS Code后端推荐 IDEA前端用 VS Code 也可以3.2 JDK17 与 SpringBoot3 的选择原因SpringBoot3 默认基于 Spring Framework 6最低要求 JDK17。相比旧版SpringBoot3 在依赖管理、GraalVM 原生镜像、Jakarta EE 命名空间等方面有明显变化。如果网上看到javax.servlet等旧包名注意 SpringBoot3 中已经改成jakarta.servlet。3.3 MySQL 准备需要提前创建好数据库CREATE DATABASE IF NOT EXISTS football_match_sale DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;MySQL 8.x 默认字符集更好支持中文和 emoji。在application.yml中连接数据库时建议加上serverTimezoneAsia/Shanghai和useUnicodetruecharacterEncodingutf8。3.4 端口规划需要保证以下端口不被占用服务默认端口说明后端 SpringBoot8080可通过server.port修改前端 Vite 开发服务器5173可通过vite.config.js修改MySQL3306本地数据库端口4. 项目目录结构与数据库设计4.1 后端项目结构football-sale-backend ├── pom.xml ├── src/main/java/com/example/footballsale │ ├── FootballSaleApplication.java │ ├── common │ │ ├── Result.java │ │ ├── ResultCode.java │ │ └── GlobalExceptionHandler.java │ ├── config │ │ ├── MybatisPlusConfig.java │ │ ├── SecurityConfig.java │ │ └── WebMvcConfig.java │ ├── controller │ │ ├── AuthController.java │ │ ├── MatchController.java │ │ ├── SessionController.java │ │ ├── SeatController.java │ │ ├── TicketController.java │ │ ├── GoodsController.java │ │ ├── CartController.java │ │ ├── OrderController.java │ │ ├── PaymentController.java │ │ ├── CouponController.java │ │ └── DashboardController.java │ ├── entity │ │ ├── User.java │ │ ├── Match.java │ │ ├── MatchSession.java │ │ ├── Stand.java │ │ ├── Seat.java │ │ ├── TicketStock.java │ │ ├── Goods.java │ │ ├── CartItem.java │ │ ├── Order.java │ │ ├── OrderItem.java │ │ ├── Coupon.java │ │ └── UserCoupon.java │ ├── mapper │ ├── service │ └── dto │ ├── LoginDTO.java │ ├── OrderCreateDTO.java │ ├── SeatLockDTO.java │ └── PaymentNotifyDTO.java └── src/main/resources ├── application.yml ├── mapper/*.xml └── sql/init.sql4.2 前端项目结构football-sale-frontend ├── index.html ├── package.json ├── vite.config.js ├── src │ ├── main.js │ ├── App.vue │ ├── api │ │ ├── request.js │ │ ├── auth.js │ │ ├── match.js │ │ ├── order.js │ │ └── dashboard.js │ ├── router │ │ └── index.js │ ├── stores │ │ ├── user.js │ │ └── cart.js │ ├── views │ │ ├── Home.vue │ │ ├── MatchList.vue │ │ ├── MatchDetail.vue │ │ ├── SeatSelect.vue │ │ ├── GoodsList.vue │ │ ├── Cart.vue │ │ ├── Checkout.vue │ │ ├── OrderList.vue │ │ ├── Login.vue │ │ └── admin │ │ ├── AdminLayout.vue │ │ ├── Dashboard.vue │ │ ├── MatchManage.vue │ │ ├── SessionManage.vue │ │ ├── SeatManage.vue │ │ ├── GoodsManage.vue │ │ ├── OrderManage.vue │ │ └── CouponManage.vue │ └── components │ ├── SeatMap.vue │ ├── MatchCard.vue │ ├── OrderStatusTag.vue │ └── ChartCard.vue4.3 数据库核心表设计核心表建议分成三类基础数据表、交易相关表、营销相关表。表名说明关键字段user用户表id, username, password, phone, real_name, role, statusmatch赛事表id, match_name, home_team, away_team, match_time, stadium, cover_imagematch_session场次表id, match_id, session_time, sale_start_time, sale_end_time, statusstand看台表id, session_id, stand_name, stand_price, total_seatsseat座位表id, stand_id, seat_row, seat_col, status(0空闲/1锁定/2已售)ticket_stock球票库存表id, session_id, stand_id, total_count, sold_count, locked_countgoods纪念品表id, goods_name, goods_desc, price, stock, imagecart_item购物车表id, user_id, goods_id, ticket_stand_id, quantity, checkedorders订单表id, order_no, user_id, total_amount, pay_amount, discount_amount, status, create_time, pay_timeorder_item订单明细表id, order_id, item_type(0球票/1商品), item_id, item_name, price, quantitycoupon优惠券表id, coupon_name, coupon_type, discount_amount, threshold_amount, stock, start_time, end_timeuser_coupon用户优惠券表id, user_id, coupon_id, status(0未使用/1已使用), get_time, use_time4.4 表关系说明一个赛事match下有多个场次match_session一个场次下有多个看台stand一个看台下有多个座位seat。用户购买球票时选择的实际是“场次 看台 座位”。订单主表orders保存一次订单的总金额和状态订单明细表order_item保存本次订单包含的球票或商品。这样设计的好处是一个订单可以同时包含球票和纪念品计价逻辑和库存扣减逻辑都放在同一事务里处理。5. 后端核心接口设计与功能实现5.1 统一返回结构所有接口统一返回ResultT避免前端每次解析多种数据结构。Data public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(success); result.setData(data); return result; } public static T ResultT error(Integer code, String message) { ResultT result new Result(); result.setCode(code); result.setMessage(message); return result; } }5.2 登录鉴权接口建议使用 Spring Security JWT 实现登录和角色鉴权。用户登录后后端返回 token前端把 token 存到 Pinia 和 localStorage每次请求由 Axios 拦截器自动携带Authorization: Bearer token。RestController RequestMapping(/api/auth) public class AuthController { PostMapping(/login) public ResultLoginResponse login(RequestBody LoginDTO dto) { // 1. 验证用户名密码 // 2. 生成 JWT token // 3. 返回用户基本信息和 token return Result.success(response); } PostMapping(/register) public ResultVoid register(RequestBody RegisterDTO dto) { // 校验用户名唯一性 // 密码加密存储BCrypt return Result.success(null); } }密码一定不能明文存储。推荐使用BCryptPasswordEncoderPasswordEncoder encoder new BCryptPasswordEncoder(); String encodedPassword encoder.encode(123456); // 校验时 boolean matches encoder.matches(rawPassword, encodedPassword);5.3 场次与座位接口选座流程建议按照“场次 → 看台 → 座位 → 锁定”的顺序实现GetMapping(/api/session/{sessionId}/seats) public ResultListSeatVO getSeatList(PathVariable Long sessionId) { // 查询当前场次下所有看台的座位状态 // 返回 seatId、standId、seatRow、seatCol、status }前端拿到座位数据后用类似影院选座的交互方式渲染SeatMap.vue。空闲座位可点击选择锁定座位置灰已售座位显示为不同颜色。5.4 下单与库存事务下单接口是整个系统最核心的部分必须加事务。业务逻辑如下校验用户是否登录。校验订单中的球票和商品库存。校验优惠券是否可用。计算订单总金额、优惠金额、实付金额。生成订单号和订单明细。扣减商品库存更新座位状态和球票库存。清空购物车对应条目。Transactional(rollbackFor Exception.class) Override public Order createOrder(OrderCreateDTO dto) { // 1. 校验 // 2. 计算价格 // 3. 保存订单主表 // 4. 保存订单明细 // 5. 扣库存 // 6. 更新座位状态 // 7. 返回订单 }注意商品库存扣减必须使用乐观锁或悲观锁避免超卖。MyBatis-Plus 中可以用版本号实现乐观锁Version private Integer version;更新库存 SQL 建议写成UPDATE goods SET stock stock - #{quantity}, version version 1 WHERE id #{goodsId} AND stock #{quantity} AND version #{version}5.5 支付接口如果只是演示建议实现“模拟支付”订单创建后用户点击“模拟支付”后端直接修改订单状态为已支付并触发支付成功后的后续逻辑。PostMapping(/api/payment/mockPay) public ResultVoid mockPay(RequestBody MockPayDTO dto) { // 1. 校验订单归属 // 2. 判断订单状态是否为待支付 // 3. 订单状态改为已支付 // 4. 扣减商品库存如果在取票/发货时扣减则不需要 // 5. 锁定座位改为已售 // 6. 发送站内通知 }如果接入真实支付回调地址必须是外网可访问地址并且需要验签。个人学习阶段不建议直接搞真实支付。5.6 批量任务实现系统里可以加一个定时任务处理“超时未支付订单自动取消”和“释放锁定座位”。用 Spring 自带的Scheduled即可Component public class OrderTimeoutTask { Scheduled(fixedDelay 60000) public void cancelTimeoutOrders() { // 查询超过15分钟未支付的订单 // 取消订单 // 释放座位状态 // 恢复商品库存 } }在启动类上添加EnableSchedulingSpringBootApplication EnableScheduling public class FootballSaleApplication { public static void main(String[] args) { SpringApplication.run(FootballSaleApplication.class, args); } }6. 前端页面与接口联调6.1 前端开发服务器代理配置前后端分离开发时前端请求会跨域。最方便的方式是在 Vite 中配置代理而不是在后端开启全局跨域。在项目根目录vite.config.js中配置import { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ plugins: [vue()], server: { host: 0.0.0.0, port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })这样设置后前端请求/api/match/list时Vite 开发服务器会自动转发到http://localhost:8080/api/match/list避免开发环境的跨域问题。6.2 Axios 请求封装在src/api/request.js中统一封装请求import axios from axios import { ElMessage } from element-plus import router from ../router import { useUserStore } from ../stores/user const request axios.create({ baseURL: /api, timeout: 15000 }) // 请求拦截器自动携带 token request.interceptors.request.use(config { const userStore useUserStore() if (userStore.token) { config.headers.Authorization Bearer ${userStore.token} } return config }) // 响应拦截器统一处理错误 request.interceptors.response.use( response { const res response.data if (res.code ! 200) { ElMessage.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) } return res }, error { if (error.response error.response.status 401) { ElMessage.error(登录状态已过期请重新登录) router.push(/login) } else { ElMessage.error(error.message || 网络错误) } return Promise.reject(error) } ) export default request6.3 赛事列表页面赛事列表页是用户进入系统后的第一个核心页面。建议用卡片式布局展示赛事包含主队、客队、比赛时间、场馆、封面图等信息。// src/api/match.js import request from ./request export function getMatchList(params) { return request.get(/match/list, { params }) } export function getMatchDetail(id) { return request.get(/match/${id}) }页面核心逻辑页面加载时调用getMatchList获取赛事列表。用户点击“查看场次”后跳转到场次选择页。场次选择页展示某个赛事下的所有场次场次状态区分未开始售卖、售卖中、已结束。6.4 选座页面选座页是整个前端交互最复杂的部分。要把后端返回的座位数据渲染成看台矩阵。建议用 CSS Grid 布局行和列对应seat_row和seat_col。template div classseat-map div v-forseat in seatList :keyseat.id classseat :classseatClass(seat) clickselectSeat(seat) {{ seat.row }}排{{ seat.col }}座 /div /div /template script setup import { ref } from vue import { getSeatList } from ../api/session const props defineProps({ sessionId: { type: Number, required: true } }) const seatList ref([]) const selectedSeats ref([]) function loadSeats() { getSeatList(props.sessionId).then(res { seatList.value res.data }) } function seatClass(seat) { if (seat.status 2) return seat-sold if (seat.status 1) return seat-locked if (selectedSeats.value.includes(seat.id)) return seat-selected return seat-available } function selectSeat(seat) { if (seat.status ! 0) return // 已选中的取消选中未选中的加入 const index selectedSeats.value.indexOf(seat.id) if (index -1) { selectedSeats.value.splice(index, 1) } else { selectedSeats.value.push(seat.id) } } /script6.5 购票与下单流程用户选完座位后进入确认订单页。页面需要展示场次信息。座位列表。球票单价。优惠券选择。是否加购纪念品。实付金额。提交订单前前端需要先检查用户是否登录import { useUserStore } from ../stores/user import { createOrder } from ../api/order const userStore useUserStore() function submitOrder() { if (!userStore.token) { ElMessage.warning(请先登录) router.push(/login) return } createOrder(orderData).then(res { ElMessage.success(订单创建成功) router.push(/checkout/ res.data.orderNo) }) }7. 接口 API 调用与批量任务验证7.1 后端接口风格所有接口统一使用/api前缀模块方法路径说明认证POST/api/auth/login用户登录赛事GET/api/match/list赛事列表场次GET/api/match/{matchId}/sessions某赛事下的场次座位GET/api/session/{sessionId}/seats某场次下的所有座位购物车GET/api/cart/list购物车列表订单POST/api/order/create创建订单支付POST/api/payment/mockPay模拟支付管理端GET/api/admin/dashboard数据统计看板7.2 后端接口验证示例以“创建订单”为例先登录获取 token再调用订单接口# 1. 登录 curl -X POST http://localhost:8080/api/auth/login \ -H Content-Type: application/json \ -d { username: testuser, password: 123456 }返回结果中包含 token。携带 token 创建订单# 2. 创建订单 curl -X POST http://localhost:8080/api/order/create \ -H Content-Type: application/json \ -H Authorization: Bearer token \ -d { userId: 1, ticketItems: [ { sessionId: 1, standId: 2, seatIds: [10, 11, 12] } ], goodsItems: [ { goodsId: 3, quantity: 2 } ], userCouponId: 5 }7.3 批量导入赛事场次管理员端可以做一个“批量导入场次”的功能前端上传 CSV 或 Excel后端解析后批量写入数据库。PostMapping(/api/admin/session/batchImport) public ResultVoid batchImport(RequestBody ListSessionImportDTO list) { // 逐条校验数据 // 批量插入场次和看台 // 生成默认座位 }批量导入的业务意义在于真实业务中一个赛季可能有几十场比赛、上百个场次手工录入效率太低。批量导入功能可以作为系统亮点写入课程设计报告。7.4 定时任务验证配合定时任务可以在数据库中手动造一张“待支付订单”把create_time改成 20 分钟前观察任务是否自动取消订单并释放座位。验证步骤如下在 MySQL 中手动修改一条待支付订单的create_time。等待定时任务执行如 1 分钟一次。检查订单状态是否变为“已取消”。检查对应座位状态是否从“锁定”变为“空闲”。检查商品库存是否恢复。UPDATE orders SET status 0, create_time DATE_SUB(NOW(), INTERVAL 20 MINUTE) WHERE order_no TEST202501010001;8. 资源占用与性能观察由于这是 SpringBoot3 Vue3 MySQL 的管理系统不需要 GPU 推理性能观察重点在 CPU、内存、数据库连接池和接口响应时间。8.1 本地运行资源占用本地开发环境运行后可以观察到项目参考占用说明后端 Java 进程200MB ~ 500MB 左右受 Maven 依赖和实际代码量影响前端 Node 进程300MB ~ 600MB 左右Vite 开发服务器和依赖预构建MySQL200MB ~ 500MB看配置和连接量IDEA/VS Code1GBIDE 自身的消耗不在业务范围内实际占用以本机环境为准。如果用 Maven 打包后运行java -jar后端内存复用会比开发模式更稳定。8.2 接口性能观察方式建议给项目加上 Spring Boot Actuator用来观察应用健康状态dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-actuator/artifactId /dependency访问http://localhost:8080/actuator/health可以看到服务是否健康。生产环境还要加入 Micrometer 监控指标。数据库慢查询日志也要打开SET GLOBAL slow_query_log ON; SET GLOBAL long_query_time 1;当某个接口响应变慢时优先看是不是 SQL 没有走索引。比如按order_no查询订单就要给order_no建唯一索引ALTER TABLE orders ADD UNIQUE INDEX uk_order_no (order_no);8.3 常见性能优化点MySQL 连接池默认配置不一定适合高并发可以用 HikariCP 调整maximum-pool-size。列表页查询不要用SELECT *只查需要的字段。分页查询必须用LIMIT推荐 MyBatis-Plus 的分页插件。订单表数据量变大后按时间做冷热数据分离。座位状态更新要及时不能让多个请求同时操作同一个座位。9. 常见问题与排查方法问题现象可能原因排查方式解决方案SpringBoot3 项目启动即失败JDK 版本过低查看启动日志中的 JDK 报错安装 JDK 17IDEA 中切换 Project SDK接口返回 401token 缺失或过期打开浏览器 Network 面板看请求头重新登录获取 token检查 Axios 拦截器前端调用接口跨域前后端端口不同查看浏览器 Console 报错信息使用 Vite proxy 或后端配置 CORS数据库连接失败MySQL 未启动或密码错误使用 Navicat/命令行连接测试确认 MySQL 服务状态和配置文件密码商品库存出现负数缺少乐观锁或库存校验不严检查更新库存 SQL使用乐观锁 stock quantity条件座位被重复售卖座位状态更新和下单不在同一事务检查Transactional是否生效确保座位更新、订单创建、库存扣减在同一个方法事务内定时任务不执行没有添加EnableScheduling检查启动类注解在启动类添加EnableScheduling中文乱码数据库字符集不是 utf8mb4查看表结构和连接参数建库用 utf8mb4URL 加 characterEncoding前端路由刷新 404history 模式部署未配置先看本地开发环境是否正常部署 Nginx 时配置 try_files 或改用 hash 模式打包后静态资源找不到前端未打包或路径配置错误检查dist目录是否存在执行npm run buildNginx 指向 dist 目录9.1 Maven 依赖下载慢或失败如果 Maven 下载依赖卡住修改settings.xml使用国内镜像mirror idaliyunmaven/id mirrorOfcentral/mirrorOf nameAliyun Maven Mirror/name urlhttps://maven.aliyun.com/repository/public/url /mirror9.2 npm 依赖安装失败npm 也推荐使用国内镜像npm config set registry https://registry.npmmirror.com安装依赖时如果报错优先删除node_modules和package-lock.json后重试rm -rf node_modules package-lock.json npm install9.3 端口被占用启动后端时如果 8080 端口被占用可以先查占用进程# Windows netstat -ano | findstr 8080 # macOS / Linux lsof -i :8080临时修改后端端口server: port: 808110. 最佳实践与工程化建议10.1 项目规范后端 Controller 只做参数接收和结果返回业务逻辑全部写在 Service 层。数据库字段用下划线命名Java 实体用驼峰命名MyBatis-Plus 会自动做驼峰映射。所有返回给前端的时间格式统一建议使用yyyy-MM-dd HH:mm:ss。金额相关字段用BigDecimal不要使用double或float。订单号不要使用数据库自增 id建议用时间戳 随机数生成例如20250101102030加 6 位随机串。10.2 第一次先小功能跑通不要一上来就写完整项目。建议按这个顺序从零搭起搭建 SpringBoot3 骨架写一个hello接口跑起来。接入 MySQL 和 MyBatis-Plus完成用户表查询。完成登录注册和 JWT 鉴权。写赛事、场次、座位三个基础模块。写购物车和订单模块。接入模拟支付。写管理端统计数据。写前端页面联调。每一步都能看到结果排查问题范围就会小很多。10.3 安全与合规提醒密码必须加密存储不能明文入库。身份证、手机号等敏感信息要脱敏展示。接口不能裸奔除登录和注册外其他接口都要做登录校验。管理员接口要单独校验角色普通用户不能调用管理员接口。涉及足球赛事素材、球队标志、球员照片时演示环境使用自己制作的占位图或免费可商用素材。在线支付功能必须使用官方沙箱或商户平台提供的测试环境不能在生产环境伪造支付结果。10.4 部署上线建议本地跑通后可以按以下方式部署后端打包mvn clean package -DskipTests java -jar target/football-sale-backend-0.0.1-SNAPSHOT.jar前端打包npm run build打包后dist目录由 Nginx 托管API 请求反向代理到后端server { listen 80; server_name your-domain.com; root /usr/share/nginx/football-sale/dist; index 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; } }11. 总结与下一步这个足球观赛售卖系统最值得学习的地方不是某一个页面效果而是从赛事管理、座位选择、购物车、下单、库存扣减、模拟支付、定时取消订单到后台统计的完整业务闭环。整个链路覆盖了 SpringBoot3 事务、MyBatis-Plus 操作、JWT 鉴权、定时任务、Vue3 组件通信、Axios 请求封装、Element Plus 表格和图表等高频技术点简历项目经验、课程设计报告和答辩演示都有内容可写。最先跑通的功能建议选“赛事列表 场次详情 选座下单”这三个功能关联最紧密能最快看到前后端联调效果。最容易踩坑的地方是座位状态并发更新和商品库存校验写代码时把Transactional用好、把乐观锁加上后续测试会顺畅很多。如果时间充裕还可以继续扩展增加订单超时取消的延迟队列。增加 Redis 缓存赛事热门场次数据。增加后台销售统计图表。增加 Excel 导出订单报表。增加微信小程序端复用现有后端 API。把这个项目完整开发一遍你对 Java 全栈项目从设计到上线的理解会明显提升。建议收藏备用开发过程中遇到具体报错可以从上面的排查清单里找思路。