基于Spring Boot+Vue+MySQL的汽车票预订系统全栈开发实战
最近在辅导学生毕业设计时发现很多同学对如何整合前后端技术栈完成一个完整的Web系统感到无从下手尤其是涉及Java后端、Vue前端、Spring Boot框架和MySQL数据库的协同开发。本文将围绕一个“汽车票网上预订系统”的毕业设计项目手把手带你从零开始完成一个功能完整、代码规范、可直接运行和答辩的实战项目。无论你是正在寻找毕业设计课题的计算机专业学生还是希望学习全栈开发技术的Java开发者都能通过本文掌握从环境搭建、数据库设计、接口开发到前端联调的全流程。1. 项目背景与核心价值1.1 项目简介汽车票网上预订系统是一个典型的B/S架构电子商务应用旨在模拟现实中的长途汽车票务服务。系统核心功能包括用户注册登录、车次查询、在线选座购票、订单管理、后台车次与线路维护等。这类系统业务逻辑清晰技术栈覆盖全面是检验学生综合运用Java Web开发、数据库设计、前端交互等能力的绝佳课题。1.2 技术选型与优势本系统采用当前企业级开发中最主流、最成熟的“前后端分离”架构。后端 (Java Spring Boot) Spring Boot极大地简化了Spring应用的初始搭建和开发过程通过自动配置和起步依赖我们可以快速构建出稳定、可扩展的RESTful API服务。它负责处理核心业务逻辑、数据持久化和安全性。前端 (Vue.js) Vue是一套用于构建用户界面的渐进式JavaScript框架以其轻量、易上手和强大的响应式数据绑定著称。采用Vue可以高效地开发出交互体验良好的单页面应用(SPA)。数据库 (MySQL) MySQL作为最流行的开源关系型数据库之一性能稳定、社区活跃完全能够满足本系统在数据存储、事务处理方面的需求。项目构建与管理 (Maven) Maven用于统一管理项目依赖、构建生命周期确保团队协作和项目部署的一致性。这种技术组合的优势在于架构清晰、职责分离、便于团队协作、前后端可以并行开发并且拥有极其丰富的社区资源和开源组件支持能有效降低学习和开发成本。2. 开发环境与工具准备在开始编码之前请确保你的本地开发环境已就绪。以下版本为本文示例环境你可以根据实际情况调整。2.1 后端环境JDK: 版本 8 或 11 (推荐11长期支持版本)。安装后配置JAVA_HOME环境变量。IDE: IntelliJ IDEA (社区版或旗舰版) 或 Eclipse (需安装Spring Tools插件)。本文以IDEA为例。构建工具: Apache Maven 3.6。安装后配置MAVEN_HOME并确保mvn -v命令能正确执行。数据库: MySQL 5.7 或 8.0。确保MySQL服务已启动并记住root用户密码。2.2 前端环境Node.js: 版本 14.x 或 16.x (LTS版本)。安装Node.js会自带包管理工具npm。包管理器: 可以使用npm但更推荐yarn或pnpm速度更快。本文使用npm。IDE: Visual Studio Code (VSCode) 或 WebStorm。VSCode轻量且插件丰富是前端开发的热门选择。2.3 辅助工具API测试: Postman 或 Insomnia用于测试后端接口。数据库管理: Navicat for MySQL, MySQL Workbench 或 DBeaver用于直观地操作数据库。版本控制: Git用于代码版本管理。可搭配Gitee或GitHub使用。环境验证命令# 检查Java java -version # 检查Maven mvn -v # 检查Node.js和npm node -v npm -v # 检查MySQL客户端需在PATH中 mysql --version3. 数据库设计与建模数据库设计是系统的基石良好的设计能保证数据一致性、完整性和查询性能。3.1 数据库概念模型 (E-R图)核心实体包括用户 (user) 系统使用者分为普通用户和管理员。汽车班次 (schedule) 具体的发车班次包含发车时间、起始站、终点站、车型、票价等。座位 (seat) 属于某个班次有座位号、类型如普通座、商务座和状态可售、已售。订单 (order) 用户购买车票后生成的凭证关联用户、班次和具体座位。支付记录 (payment) 记录订单的支付状态和详情。实体间关系一个用户可以下多个订单一个订单对应一个班次和一个座位一个班次拥有多个座位。3.2 物理表结构SQL在MySQL中创建数据库bus_ticket_db并执行以下SQL语句建表。-- 创建数据库 CREATE DATABASE IF NOT EXISTS bus_ticket_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE bus_ticket_db; -- 用户表 CREATE TABLE sys_user ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键ID, username varchar(50) NOT NULL COMMENT 用户名登录用, password varchar(255) NOT NULL COMMENT 密码加密存储, real_name varchar(50) DEFAULT NULL COMMENT 真实姓名, phone varchar(20) DEFAULT NULL COMMENT 手机号, id_card varchar(20) DEFAULT NULL COMMENT 身份证号, avatar varchar(500) DEFAULT NULL COMMENT 头像URL, role varchar(20) NOT NULL DEFAULT USER COMMENT 角色USER-普通用户ADMIN-管理员, status tinyint(1) NOT NULL DEFAULT 1 COMMENT 状态0-禁用1-正常, 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), UNIQUE KEY uk_phone (phone) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT系统用户表; -- 插入初始管理员用户 (密码明文为admin123实际应为BCrypt加密后的值) INSERT INTO sys_user (username, password, real_name, role) VALUES (admin, $2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTV5UiC, 系统管理员, ADMIN); -- 汽车班次表 CREATE TABLE bus_schedule ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 班次ID, bus_number varchar(50) NOT NULL COMMENT 车牌号/班次号, departure_station varchar(100) NOT NULL COMMENT 始发站, arrival_station varchar(100) NOT NULL COMMENT 终点站, departure_time datetime NOT NULL COMMENT 发车时间, estimated_arrival_time datetime NOT NULL COMMENT 预计到达时间, total_seats int(11) NOT NULL COMMENT 总座位数, available_seats int(11) NOT NULL COMMENT 剩余座位数, price decimal(10,2) NOT NULL COMMENT 票价, bus_type varchar(50) DEFAULT 普通大巴 COMMENT 车型, status varchar(20) NOT NULL DEFAULT NORMAL COMMENT 状态NORMAL-正常CANCELLED-已取消, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_departure_time (departure_time), KEY idx_route (departure_station, arrival_station) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT汽车班次表; -- 座位表 CREATE TABLE bus_seat ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 座位ID, schedule_id bigint(20) NOT NULL COMMENT 所属班次ID, seat_number varchar(10) NOT NULL COMMENT 座位号如A01, B12, seat_type varchar(20) DEFAULT NORMAL COMMENT 座位类型NORMAL-普通VIP-商务, status varchar(20) NOT NULL DEFAULT AVAILABLE COMMENT 状态AVAILABLE-可售LOCKED-锁定选座中SOLD-已售, lock_expire_time datetime DEFAULT NULL COMMENT 锁定过期时间, PRIMARY KEY (id), UNIQUE KEY uk_schedule_seat (schedule_id, seat_number), KEY idx_schedule_status (schedule_id, status), CONSTRAINT fk_seat_schedule FOREIGN KEY (schedule_id) REFERENCES bus_schedule (id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT座位表; -- 订单表 CREATE TABLE bus_order ( id varchar(32) NOT NULL COMMENT 订单号业务主键, user_id bigint(20) NOT NULL COMMENT 用户ID, schedule_id bigint(20) NOT NULL COMMENT 班次ID, seat_id bigint(20) NOT NULL COMMENT 座位ID, passenger_name varchar(50) NOT NULL COMMENT 乘客姓名, passenger_id_card varchar(20) NOT NULL COMMENT 乘客身份证, total_amount decimal(10,2) NOT NULL COMMENT 订单总金额, order_status varchar(20) NOT NULL DEFAULT UNPAID COMMENT 订单状态UNPAID-待支付PAID-已支付CANCELLED-已取消REFUNDED-已退款, pay_time datetime 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), KEY idx_user_id (user_id), KEY idx_create_time (create_time), CONSTRAINT fk_order_user FOREIGN KEY (user_id) REFERENCES sys_user (id), CONSTRAINT fk_order_schedule FOREIGN KEY (schedule_id) REFERENCES bus_schedule (id), CONSTRAINT fk_order_seat FOREIGN KEY (seat_id) REFERENCES bus_seat (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单表;设计要点说明密码安全 用户密码必须加密存储示例中使用的是BCrypt加密后的密文。绝对禁止明文存储。索引优化 在departure_time,departure_station等查询频繁的字段上创建索引提升查询性能。外键约束 使用外键保证数据引用完整性例如订单必须关联有效的用户、班次和座位。业务主键 订单号id使用字符串如UUID或雪花算法ID便于业务展示和传递。数据字典 状态字段如order_status,seat_status使用明确的字符串常量提高可读性。4. Spring Boot后端项目搭建与核心开发4.1 创建Spring Boot项目使用Spring Initializr (https://start.spring.io) 或IDEA内置的Spring Initializr创建项目。Project: Maven ProjectLanguage: JavaSpring Boot: 2.7.x (稳定版本)Group: com.busticketArtifact: bus-ticket-systemPackaging: JarJava: 11Dependencies: 选择Spring Web,Spring Data JPA,MySQL Driver,Lombok。生成项目后用IDEA打开。4.2 项目结构与关键配置项目标准Maven结构如下bus-ticket-system ├── src/main/java/com/busticket │ ├── BusTicketSystemApplication.java // 启动类 │ ├── config/ // 配置类如WebConfig, SecurityConfig │ ├── controller/ // 控制器层 │ ├── service/ // 业务逻辑层 │ │ └── impl/ // 业务实现类 │ ├── repository/ // 数据访问层 (JPA) │ ├── model/entity/ // 实体类 │ ├── model/dto/ // 数据传输对象 │ ├── model/vo/ // 视图对象 │ └── utils/ // 工具类 ├── src/main/resources │ ├── application.yml // 主配置文件 │ └── static/ // 静态资源 └── pom.xml // Maven依赖管理配置文件application.ymlserver: port: 8080 servlet: context-path: /api spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/bus_ticket_db?useUnicodetruecharacterEncodingutf-8serverTimezoneAsia/Shanghai username: root password: your_password_here # 替换为你的MySQL密码 jpa: hibernate: ddl-auto: update # 开发环境可用update生产环境用validate或none show-sql: true properties: hibernate: format_sql: true database-platform: org.hibernate.dialect.MySQL8Dialect # 自定义配置 app: jwt: secret: yourSuperSecretKeyForJWT123!# # JWT密钥务必修改并保密 expiration: 86400000 # token有效期单位毫秒 (24小时)4.3 核心实体类与JPA映射使用JPA注解将数据库表映射为Java实体类。用户实体 (User.java):package com.busticket.model.entity; import lombok.Data; import javax.persistence.*; import java.time.LocalDateTime; Entity Table(name sys_user) Data public class User { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; Column(unique true, nullable false, length 50) private String username; Column(nullable false) private String password; private String realName; private String phone; private String idCard; private String avatar; Column(nullable false) private String role USER; // USER, ADMIN Column(nullable false) private Boolean status true; Column(updatable false) private LocalDateTime createTime; private LocalDateTime updateTime; PrePersist protected void onCreate() { createTime LocalDateTime.now(); updateTime LocalDateTime.now(); } PreUpdate protected void onUpdate() { updateTime LocalDateTime.now(); } }班次实体 (Schedule.java) 和 订单实体 (Order.java)依此类推使用OneToMany,ManyToOne等注解定义关联关系。4.4 实现用户登录与JWT认证在Web系统中认证和授权是首要安全环节。我们采用无状态的JWT (JSON Web Token) 方案。1. 添加JWT依赖 (pom.xml):dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-api/artifactId version0.11.5/version /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-impl/artifactId version0.11.5/version scoperuntime/scope /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-jackson/artifactId version0.11.5/version scoperuntime/scope /dependency2. 创建JWT工具类 (JwtUtil.java):package com.busticket.utils; import io.jsonwebtoken.*; import io.jsonwebtoken.security.Keys; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.security.Key; import java.util.Date; Component public class JwtUtil { Value(${app.jwt.secret}) private String secret; Value(${app.jwt.expiration}) private Long expiration; private Key getSigningKey() { return Keys.hmacShaKeyFor(secret.getBytes()); } public String generateToken(String username, String role) { Date now new Date(); Date expiryDate new Date(now.getTime() expiration); return Jwts.builder() .setSubject(username) .claim(role, role) .setIssuedAt(now) .setExpiration(expiryDate) .signWith(getSigningKey(), SignatureAlgorithm.HS256) .compact(); } public String getUsernameFromToken(String token) { Claims claims Jwts.parserBuilder() .setSigningKey(getSigningKey()) .build() .parseClaimsJws(token) .getBody(); return claims.getSubject(); } public boolean validateToken(String token) { try { Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token); return true; } catch (JwtException | IllegalArgumentException e) { // 日志记录异常 return false; } } }3. 创建登录接口 (AuthController.java):package com.busticket.controller; import com.busticket.model.dto.LoginRequest; import com.busticket.model.dto.LoginResponse; import com.busticket.model.entity.User; import com.busticket.repository.UserRepository; import com.busticket.utils.JwtUtil; import lombok.RequiredArgsConstructor; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import java.util.Optional; RestController RequestMapping(/auth) RequiredArgsConstructor public class AuthController { private final UserRepository userRepository; private final BCryptPasswordEncoder passwordEncoder; private final JwtUtil jwtUtil; PostMapping(/login) public LoginResponse login(Valid RequestBody LoginRequest request) { OptionalUser userOpt userRepository.findByUsername(request.getUsername()); if (userOpt.isEmpty()) { throw new RuntimeException(用户名或密码错误); } User user userOpt.get(); if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) { throw new RuntimeException(用户名或密码错误); } if (!user.getStatus()) { throw new RuntimeException(用户已被禁用); } String token jwtUtil.generateToken(user.getUsername(), user.getRole()); return new LoginResponse(token, user.getUsername(), user.getRole()); } }4. 配置Spring Security (简化版):创建一个配置类SecurityConfig.java配置密码编码器、放行登录接口、对其他接口进行JWT令牌验证。由于篇幅限制这里不展开完整代码核心是自定义一个JwtAuthenticationFilter来拦截请求并验证Token。4.5 实现车次查询与购票业务这是系统的核心业务逻辑涉及事务管理和并发控制。1. 车次查询接口 (ScheduleController.java):RestController RequestMapping(/schedules) RequiredArgsConstructor public class ScheduleController { private final ScheduleService scheduleService; GetMapping public PageResultScheduleVO querySchedules( RequestParam String departure, RequestParam String arrival, RequestParam DateTimeFormat(pattern yyyy-MM-dd) LocalDate date, RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer size) { return scheduleService.querySchedules(departure, arrival, date, page, size); } }2. 购票接口 (OrderController.java) - 重点注意并发问题:购票是典型的高并发场景需要防止超卖同一座位被多人购买。PostMapping(/create) public Result createOrder(Valid RequestBody CreateOrderDTO dto, RequestHeader(Authorization) String token) { // 1. 验证用户身份 (从token中解析userId) Long userId getUserIdFromToken(token); // 2. 调用服务层方法该方法必须加 Transactional 和锁机制 String orderId orderService.createOrder(userId, dto); return Result.success(下单成功, orderId); }服务层购票方法 (OrderServiceImpl.java):Override Transactional(rollbackFor Exception.class) public String createOrder(Long userId, CreateOrderDTO dto) { // 1. 查询班次和座位信息并加锁悲观锁或乐观锁 Schedule schedule scheduleRepository.findByIdWithLock(dto.getScheduleId()) .orElseThrow(() - new RuntimeException(班次不存在)); Seat seat seatRepository.findByIdWithLock(dto.getSeatId()) .orElseThrow(() - new RuntimeException(座位不存在)); // 2. 校验班次状态、座位状态、余票 if (!NORMAL.equals(schedule.getStatus())) { throw new RuntimeException(该班次已取消); } if (!AVAILABLE.equals(seat.getStatus())) { throw new RuntimeException(该座位已被预订或售出); } if (schedule.getAvailableSeats() 0) { throw new RuntimeException(余票不足); } // 3. 扣减余票更新座位状态在同一个事务内 schedule.setAvailableSeats(schedule.getAvailableSeats() - 1); scheduleRepository.save(schedule); seat.setStatus(SOLD); seatRepository.save(seat); // 4. 生成订单 Order order new Order(); order.setId(generateOrderId()); // 使用雪花算法生成订单号 order.setUserId(userId); order.setScheduleId(schedule.getId()); order.setSeatId(seat.getId()); order.setPassengerName(dto.getPassengerName()); order.setPassengerIdCard(dto.getPassengerIdCard()); order.setTotalAmount(schedule.getPrice()); order.setOrderStatus(UNPAID); orderRepository.save(order); // 5. (可选) 发送创建订单成功消息到消息队列触发后续支付流程 // rabbitTemplate.convertAndSend(order.exchange, order.created, order.getId()); return order.getId(); }关键点这里使用了findByIdWithLock自定义方法使用SELECT ... FOR UPDATE对记录加悲观锁确保在事务提交前其他线程无法修改这条记录这是防止超卖的一种有效方式。在高并发场景下也可以考虑使用乐观锁版本号或分布式锁如Redis。5. Vue前端项目开发与联调5.1 创建Vue项目使用Vue CLI快速搭建项目。# 全局安装Vue CLI如果未安装 npm install -g vue/cli # 创建项目 vue create bus-ticket-frontend # 选择 Manually select features # 勾选 Babel, Router, Vuex, CSS Pre-processors (如Sass), Linter/Formatter # 选择 Vue 2 或 Vue 3 (本文以Vue 3 Composition API为例) # 其余选项按需选择或默认 cd bus-ticket-frontend npm install axios element-plus --save # 安装HTTP库和UI组件库5.2 项目结构规划src/ ├── api/ // 所有后端接口请求封装 │ └── index.js ├── assets/ // 静态资源 ├── components/ // 公共组件 ├── router/ // 路由配置 ├── store/ // Vuex状态管理 ├── utils/ // 工具函数如request拦截器 ├── views/ // 页面组件 │ ├── Login.vue │ ├── Home.vue │ ├── ScheduleQuery.vue │ ├── SeatSelection.vue │ └── OrderList.vue └── App.vue5.3 封装Axios与API请求在src/utils/request.js中创建axios实例配置请求/响应拦截器用于自动添加JWT Token和处理通用错误。import axios from axios; import { ElMessage } from element-plus; import router from /router; const service axios.create({ baseURL: process.env.VUE_APP_BASE_API || http://localhost:8080/api, timeout: 10000, }); // 请求拦截器 service.interceptors.request.use( config { const token localStorage.getItem(token); if (token) { config.headers[Authorization] Bearer ${token}; } return config; }, error { console.error(Request Error:, error); return Promise.reject(error); } ); // 响应拦截器 service.interceptors.response.use( response { const res response.data; // 假设后端统一返回格式为 { code: 200, message: success, data: {} } if (res.code ! 200) { ElMessage.error(res.message || Error); // 如果是401未授权跳转到登录页 if (res.code 401) { localStorage.removeItem(token); router.push(/login); } return Promise.reject(new Error(res.message || Error)); } else { return res.data; // 直接返回业务数据 } }, error { console.error(Response Error:, error); ElMessage.error(error.message || 网络请求失败); return Promise.reject(error); } ); export default service;在src/api/index.js中封装具体的业务接口。import request from /utils/request; export function login(data) { return request({ url: /auth/login, method: post, data }); } export function getScheduleList(params) { return request({ url: /schedules, method: get, params }); } export function createOrder(data) { return request({ url: /orders/create, method: post, data }); }5.4 实现车次查询与选座页面车次查询页面 (ScheduleQuery.vue) 核心代码片段template div classschedule-query el-form :modelqueryForm inline submit.preventhandleQuery el-form-item label出发城市 el-input v-modelqueryForm.departure placeholder如北京/el-input /el-form-item el-form-item label到达城市 el-input v-modelqueryForm.arrival placeholder如上海/el-input /el-form-item el-form-item label出发日期 el-date-picker v-modelqueryForm.date typedate value-formatyyyy-MM-dd/el-date-picker /el-form-item el-form-item el-button typeprimary clickhandleQuery查询/el-button /el-form-item /el-form el-table :datascheduleList v-loadingloading el-table-column propbusNumber label班次号/el-table-column el-table-column propdepartureStation label始发站/el-table-column el-table-column proparrivalStation label终点站/el-table-column el-table-column propdepartureTime label发车时间 template #default{ row } {{ formatDateTime(row.departureTime) }} /template /el-table-column el-table-column propprice label票价元/el-table-column el-table-column propavailableSeats label余票/el-table-column el-table-column label操作 template #default{ row } el-button sizesmall clickgoToSeatSelection(row.id)选座购票/el-button /template /el-table-column /el-table el-pagination current-changehandlePageChange :current-pagecurrentPage :page-sizepageSize :totaltotal layoutprev, pager, next /el-pagination /div /template script setup import { ref, onMounted } from vue; import { getScheduleList } from /api; import { useRouter } from vue-router; import { ElMessage } from element-plus; const router useRouter(); const queryForm ref({ departure: , arrival: , date: new Date().toISOString().split(T)[0] // 默认今天 }); const scheduleList ref([]); const loading ref(false); const currentPage ref(1); const pageSize 10; const total ref(0); const handleQuery async () { if (!queryForm.value.departure || !queryForm.value.arrival) { ElMessage.warning(请输入出发地和目的地); return; } loading.value true; try { const params { ...queryForm.value, page: currentPage.value, size: pageSize }; const res await getScheduleList(params); scheduleList.value res.list; total.value res.total; } catch (error) { console.error(error); } finally { loading.value false; } }; const goToSeatSelection (scheduleId) { router.push({ path: /seat-selection, query: { scheduleId } }); }; onMounted(() { handleQuery(); // 页面加载时默认查询一次 }); /script选座页面 (SeatSelection.vue)则需要通过scheduleId查询该班次的所有座位状态并用一个网格Grid或表格渲染出来可用不同颜色区分“可售”、“已售”状态。用户点击可售座位后弹出表单填写乘客信息并调用创建订单接口。6. 系统部署与运行6.1 后端打包与运行打包: 在项目根目录执行mvn clean package -DskipTests会在target目录生成bus-ticket-system-0.0.1-SNAPSHOT.jar。运行:java -jar bus-ticket-system-0.0.1-SNAPSHOT.jar。确保MySQL服务已启动且配置文件中的数据库连接信息正确。验证: 浏览器访问http://localhost:8080/api/auth/login(POST请求需用Postman测试)或访问http://localhost:8080/api/schedules?departure北京arrival上海date2023-10-27看是否能返回数据。6.2 前端打包与运行开发环境运行:npm run serve访问http://localhost:8081(端口可能不同)。生产打包: 修改.env.production文件设置VUE_APP_BASE_API为你的后端生产环境地址。然后执行npm run build生成静态文件在dist目录。部署: 可以将dist目录下的文件部署到Nginx、Apache等Web服务器或使用Docker容器化部署。6.3 前后端联调启动后端Spring Boot应用。启动前端开发服务器 (npm run serve)。在前端页面进行登录、查询、购票等操作。使用浏览器开发者工具的Network面板查看所有HTTP请求和响应这是调试前后端交互问题的关键。7. 常见问题与解决方案 (FAQ)问题现象可能原因解决方案前端访问后端接口出现CORS错误浏览器同源策略限制前端(localhost:8081)访问后端(localhost:8080)端口不同。在后端Spring Boot中配置CORS。添加CrossOrigin注解或全局CORS配置类。启动Spring Boot报java.net.ConnectException: Connection refusedMySQL数据库服务未启动或连接URL、用户名、密码错误。检查MySQL服务状态确认application.yml中的数据库配置正确。Vue项目运行时报Error: Cannot find module ‘xxx’依赖未安装或node_modules损坏。删除node_modules和package-lock.json重新执行npm install。JPA执行更新操作字段未更新实体类未提供setter方法或使用了Data但其他注解冲突。确保实体类有正确的setter方法或检查是否在非事务方法中执行了更新。购票时出现“座位已售出”但查询显示有余票高并发下多个请求同时通过了余票检查。使用数据库悲观锁(SELECT ... FOR UPDATE)、乐观锁(版本号)或在应用层使用分布式锁。前端页面刷新后Vuex状态丢失Vuex状态存储在内存中页面刷新会重置。将需要持久化的状态如用户token、基本信息同步存储到localStorage或sessionStorage。打包后的前端页面访问后端API 404生产环境API地址配置错误或Nginx未正确代理API请求。检查打包时设置的VUE_APP_BASE_API并配置Nginx将/api路径的请求反向代理到后端服务。8. 项目扩展与优化建议 (毕业设计加分项)一个基础的CRUD系统只能算及格要获得高分需要在以下方面进行深化支付集成 模拟支付流程。集成支付宝/微信支付沙箱环境实现从创建订单、调用支付接口、处理支付回调、更新订单状态的完整闭环。短信/邮件通知 使用阿里云短信服务或邮件服务在用户注册、购票成功、发车前提醒等环节发送通知。Redis缓存 将热门线路的车次信息、站点信息缓存到Redis中大幅减轻数据库压力提升查询性能。Elasticsearch搜索 如果班次数据量大查询条件复杂可以引入Elasticsearch实现更高效、更灵活的车次搜索功能。后台管理系统 为管理员开发一个独立的Vue后台管理项目实现班次管理、用户管理、订单统计、数据可视化使用ECharts等功能。Docker容器化部署 编写Dockerfile和docker-compose.yml将MySQL、Redis、后端应用、前端应用全部容器化实现一键部署体现运维能力。API文档 使用Swagger/OpenAPI生成在线API文档方便前端对接和答辩演示。单元测试 为Service层关键方法编写JUnit单元测试保证核心业务逻辑的正确性。压力测试 使用JMeter对购票接口进行压力测试分析系统瓶颈并提出优化方案这在答辩时是很大的亮点。完成这个项目后你不仅能够提交一份高质量的毕业设计更能系统地掌握从前端到后端、从数据库到部署的完整全栈开发技能。建议你在GitHub或Gitee上创建仓库来管理代码良好的提交记录和README文档也是加分项。