SpringBoot3+Vue3足球观赛售卖系统全栈实战项目解析
2026/9/6 7:39:45 网站建设 项目流程

如果你正在找 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中连接数据库时,建议加上serverTimezone=Asia/ShanghaiuseUnicode=true&characterEncoding=utf8

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.sql

4.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.vue

4.3 数据库核心表设计

核心表建议分成三类:基础数据表、交易相关表、营销相关表。

表名说明关键字段
user用户表id, username, password, phone, real_name, role, status
match赛事表id, match_name, home_team, away_team, match_time, stadium, cover_image
match_session场次表id, match_id, session_time, sale_start_time, sale_end_time, status
stand看台表id, session_id, stand_name, stand_price, total_seats
seat座位表id, stand_id, seat_row, seat_col, status(0空闲/1锁定/2已售)
ticket_stock球票库存表id, session_id, stand_id, total_count, sold_count, locked_count
goods纪念品表id, goods_name, goods_desc, price, stock, image
cart_item购物车表id, user_id, goods_id, ticket_stand_id, quantity, checked
orders订单表id, order_no, user_id, total_amount, pay_amount, discount_amount, status, create_time, pay_time
order_item订单明细表id, order_id, item_type(0球票/1商品), item_id, item_name, price, quantity
coupon优惠券表id, coupon_name, coupon_type, discount_amount, threshold_amount, stock, start_time, end_time
user_coupon用户优惠券表id, user_id, coupon_id, status(0未使用/1已使用), get_time, use_time

4.4 表关系说明

一个赛事match下有多个场次match_session,一个场次下有多个看台stand,一个看台下有多个座位seat。用户购买球票时,选择的实际是“场次 + 看台 + 座位”。订单主表orders保存一次订单的总金额和状态,订单明细表order_item保存本次订单包含的球票或商品。这样设计的好处是:一个订单可以同时包含球票和纪念品,计价逻辑和库存扣减逻辑都放在同一事务里处理。

5. 后端核心接口设计与功能实现

5.1 统一返回结构

所有接口统一返回Result<T>,避免前端每次解析多种数据结构。

@Data public class Result<T> { private Integer code; private String message; private T data; public static <T> Result<T> success(T data) { Result<T> result = new Result<>(); result.setCode(200); result.setMessage("success"); result.setData(data); return result; } public static <T> Result<T> error(Integer code, String message) { Result<T> 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 Result<LoginResponse> login(@RequestBody LoginDTO dto) { // 1. 验证用户名密码 // 2. 生成 JWT token // 3. 返回用户基本信息和 token return Result.success(response); } @PostMapping("/register") public Result<Void> register(@RequestBody RegisterDTO dto) { // 校验用户名唯一性 // 密码加密存储(BCrypt) return Result.success(null); } }

密码一定不能明文存储。推荐使用BCryptPasswordEncoder

PasswordEncoder encoder = new BCryptPasswordEncoder(); String encodedPassword = encoder.encode("123456"); // 校验时 boolean matches = encoder.matches(rawPassword, encodedPassword);

5.3 场次与座位接口

选座流程建议按照“场次 → 看台 → 座位 → 锁定”的顺序实现:

@GetMapping("/api/session/{sessionId}/seats") public Result<List<SeatVO>> getSeatList(@PathVariable Long sessionId) { // 查询当前场次下所有看台的座位状态 // 返回 seatId、standId、seatRow、seatCol、status }

前端拿到座位数据后,用类似影院选座的交互方式渲染SeatMap.vue。空闲座位可点击选择,锁定座位置灰,已售座位显示为不同颜色。

5.4 下单与库存事务

下单接口是整个系统最核心的部分,必须加事务。业务逻辑如下:

  1. 校验用户是否登录。
  2. 校验订单中的球票和商品库存。
  3. 校验优惠券是否可用。
  4. 计算订单总金额、优惠金额、实付金额。
  5. 生成订单号和订单明细。
  6. 扣减商品库存,更新座位状态和球票库存。
  7. 清空购物车对应条目。
@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 Result<Void> 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分钟未支付的订单 // 取消订单 // 释放座位状态 // 恢复商品库存 } }

在启动类上添加@EnableScheduling

@SpringBootApplication @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 request

6.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}`) }

页面核心逻辑:

  1. 页面加载时调用getMatchList获取赛事列表。
  2. 用户点击“查看场次”后,跳转到场次选择页。
  3. 场次选择页展示某个赛事下的所有场次,场次状态区分未开始售卖、售卖中、已结束。

6.4 选座页面

选座页是整个前端交互最复杂的部分。要把后端返回的座位数据渲染成看台矩阵。建议用 CSS Grid 布局,行和列对应seat_rowseat_col

<template> <div class="seat-map"> <div v-for="seat in seatList" :key="seat.id" class="seat" :class="seatClass(seat)" @click="selectSeat(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) } } </script>

6.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 Result<Void> batchImport(@RequestBody List<SessionImportDTO> list) { // 逐条校验数据 // 批量插入场次和看台 // 生成默认座位 }

批量导入的业务意义在于:真实业务中一个赛季可能有几十场比赛、上百个场次,手工录入效率太低。批量导入功能可以作为系统亮点写入课程设计报告。

7.4 定时任务验证

配合定时任务,可以在数据库中手动造一张“待支付订单”,把create_time改成 20 分钟前,观察任务是否自动取消订单并释放座位。验证步骤如下:

  1. 在 MySQL 中手动修改一条待支付订单的create_time
  2. 等待定时任务执行(如 1 分钟一次)。
  3. 检查订单状态是否变为“已取消”。
  4. 检查对应座位状态是否从“锁定”变为“空闲”。
  5. 检查商品库存是否恢复。
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 Code1GB+IDE 自身的消耗不在业务范围内

实际占用以本机环境为准。如果用 Maven 打包后运行java -jar,后端内存复用会比开发模式更稳定。

8.2 接口性能观察方式

建议给项目加上 Spring Boot Actuator,用来观察应用健康状态:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-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 17+,IDEA 中切换 Project SDK
接口返回 401token 缺失或过期打开浏览器 Network 面板,看请求头重新登录获取 token,检查 Axios 拦截器
前端调用接口跨域前后端端口不同查看浏览器 Console 报错信息使用 Vite proxy 或后端配置 CORS
数据库连接失败MySQL 未启动或密码错误使用 Navicat/命令行连接测试确认 MySQL 服务状态和配置文件密码
商品库存出现负数缺少乐观锁或库存校验不严检查更新库存 SQL使用乐观锁 +stock >= quantity条件
座位被重复售卖座位状态更新和下单不在同一事务检查@Transactional是否生效确保座位更新、订单创建、库存扣减在同一个方法事务内
定时任务不执行没有添加@EnableScheduling检查启动类注解在启动类添加@EnableScheduling
中文乱码数据库字符集不是 utf8mb4查看表结构和连接参数建库用 utf8mb4,URL 加 characterEncoding
前端路由刷新 404history 模式部署未配置先看本地开发环境是否正常部署 Nginx 时配置 try_files 或改用 hash 模式
打包后静态资源找不到前端未打包或路径配置错误检查dist目录是否存在执行npm run build,Nginx 指向 dist 目录

9.1 Maven 依赖下载慢或失败

如果 Maven 下载依赖卡住,修改settings.xml,使用国内镜像:

<mirror> <id>aliyunmaven</id> <mirrorOf>central</mirrorOf> <name>Aliyun Maven Mirror</name> <url>https://maven.aliyun.com/repository/public</url> </mirror>

9.2 npm 依赖安装失败

npm 也推荐使用国内镜像:

npm config set registry https://registry.npmmirror.com

安装依赖时如果报错,优先删除node_modulespackage-lock.json后重试:

rm -rf node_modules package-lock.json npm install

9.3 端口被占用

启动后端时如果 8080 端口被占用,可以先查占用进程:

# Windows netstat -ano | findstr 8080 # macOS / Linux lsof -i :8080

临时修改后端端口:

server: port: 8081

10. 最佳实践与工程化建议

10.1 项目规范

  • 后端 Controller 只做参数接收和结果返回,业务逻辑全部写在 Service 层。
  • 数据库字段用下划线命名,Java 实体用驼峰命名,MyBatis-Plus 会自动做驼峰映射。
  • 所有返回给前端的时间格式统一,建议使用yyyy-MM-dd HH:mm:ss
  • 金额相关字段用BigDecimal,不要使用doublefloat
  • 订单号不要使用数据库自增 id,建议用时间戳 + 随机数生成,例如20250101102030加 6 位随机串。

10.2 第一次先小功能跑通

不要一上来就写完整项目。建议按这个顺序从零搭起:

  1. 搭建 SpringBoot3 骨架,写一个hello接口,跑起来。
  2. 接入 MySQL 和 MyBatis-Plus,完成用户表查询。
  3. 完成登录注册和 JWT 鉴权。
  4. 写赛事、场次、座位三个基础模块。
  5. 写购物车和订单模块。
  6. 接入模拟支付。
  7. 写管理端统计数据。
  8. 写前端页面联调。

每一步都能看到结果,排查问题范围就会小很多。

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 全栈项目从设计到上线的理解会明显提升。建议收藏备用,开发过程中遇到具体报错,可以从上面的排查清单里找思路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询