☰
Spring Boot + Vue 3 酒店管理系统全栈开发实战:从环境搭建到生产部署
2026/10/15 7:18:04 网站建设 项目流程

在实际 Java Web 项目开发中,一个完整的、前后端分离的管理系统是检验技术综合运用能力的绝佳场景。酒店客房管理系统正是这样一个典型项目,它涵盖了用户管理、客房信息维护、订单处理、数据统计等核心业务模块,非常适合用于学习 Spring Boot 后端开发与 Vue 3 前端框架的集成。很多开发者拿到源码后,往往卡在环境配置、依赖版本、前后端联调等环节,导致项目无法成功运行。本文将围绕一个基于 Spring Boot 和 Vue 3 的酒店客房管理系统,从零开始,详细拆解其搭建过程、核心代码逻辑、常见问题排查以及生产环境部署的注意事项,目标是让你不仅能成功运行项目,更能理解其背后的设计思路和工程实践。

1. 理解项目架构与技术选型

在动手搭建之前,必须先理清整个项目的技术栈和架构设计。这决定了后续环境准备、代码编写和问题排查的方向。

1.1 为什么选择 Spring Boot + Vue 3

Spring Boot 以其“约定大于配置”的理念,极大地简化了传统 Spring 应用的初始搭建和开发过程。它内嵌了 Tomcat 等 Web 服务器,无需部署 WAR 包,通过一个main方法就能启动一个独立的、生产级别的应用。对于酒店客房管理系统这类需要快速迭代、包含复杂业务逻辑的后台服务,Spring Boot 提供了完善的 Web、数据访问、安全、缓存等模块支持。

Vue 3 作为现代前端框架,其组合式 API 提供了更好的逻辑复用和类型推断能力,配合 TypeScript 使用,能构建出更健壮、可维护性更高的前端应用。前后端分离的架构使得后端可以专注于提供 RESTful API,前端则独立开发、部署,两者通过 HTTP 协议进行数据交互,职责清晰,便于团队协作。

1.2 项目核心模块划分

一个典型的酒店客房管理系统通常包含以下模块:

  • 后端 (Spring Boot):
    • 实体层 (Entity/Model):对应数据库表,如User、Room、Order。
    • 数据访问层 (Repository/DAO):使用 Spring Data JPA 或 MyBatis-Plus 进行数据库操作。
    • 业务逻辑层 (Service):封装核心业务规则,如客房预订逻辑、价格计算。
    • 控制层 (Controller):接收前端 HTTP 请求,调用 Service,返回 JSON 数据。
    • 配置与工具类:数据库连接、跨域配置、权限拦截、工具函数等。
  • 前端 (Vue 3):
    • 视图组件 (Components):如登录页、客房列表页、订单表单页。
    • 状态管理 (Pinia):集中管理用户登录状态、客房数据等应用级状态。
    • 路由管理 (Vue Router):定义页面路径与组件的映射关系。
    • HTTP 客户端 (Axios):封装与后端 API 的通信。
    • UI 组件库:如 Element Plus,用于快速构建界面。

1.3 开发环境与工具准备

在开始编码前,请确保你的本地开发环境已就绪。以下是推荐的环境清单:

工具/环境推荐版本说明
JDK11 或 17Spring Boot 3.x 需要 JDK 17+,Spring Boot 2.x 兼容 JDK 8/11。建议使用 JDK 17 以获得长期支持。
Maven3.6+用于管理 Java 项目依赖、构建和打包。
Node.js18+Vue 3 的运行和构建环境。同时会安装 npm 包管理器。
IDEIntelliJ IDEA / VS CodeIDEA 用于后端 Java 开发,VS Code 用于前端 Vue 开发。
数据库MySQL 5.7+ / 8.0项目的主要数据存储。也可根据项目源码适配 PostgreSQL。
Git最新版用于版本控制和获取项目源码。

注意:版本兼容性是项目能否成功启动的关键。如果项目源码未明确说明,建议优先使用上述推荐版本,避免因版本过高或过低导致依赖冲突。

2. 后端 Spring Boot 项目搭建与配置

我们将从创建一个标准的 Spring Boot 项目开始,逐步集成必要的依赖和配置。

2.1 使用 Spring Initializr 初始化项目

最便捷的方式是使用 start.spring.io 或 IDE 内置的 Spring Initializr 创建项目。

  1. 选择参数:

    • Project: Maven Project
    • Language: Java
    • Spring Boot: 选择稳定版本(如 3.1.x 或 2.7.x,需与源码匹配)
    • Project Metadata:
      • Group:com.example(可改为你的组织名,如com.hotel)
      • Artifact:hotel-management
      • Name:hotel-management
      • Package name:com.example.hotelmanagement
    • Packaging: Jar
    • Java: 17
  2. 添加依赖:根据酒店管理系统的需求,通常需要添加以下依赖:

    • Spring Web: 构建 RESTful API。
    • Spring Data JPA: 简化数据库操作。
    • MySQL Driver: 连接 MySQL 数据库。
    • Lombok: 通过注解减少 getter/setter 等样板代码。
    • Spring Boot DevTools: 开发时热加载。
    • Validation: 参数校验。

生成项目后,使用 IDE 导入 Maven 项目。

2.2 核心配置文件详解

项目根目录下的application.properties或application.yml是 Spring Boot 的核心配置文件。对于酒店管理系统,典型配置如下:

# application.yml server: port: 8080 # 后端服务端口 spring: application: name: hotel-management datasource: url: jdbc:mysql://localhost:3306/hotel_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update # 开发环境可用 update,生产环境务必改为 validate 或 none show-sql: true # 开发时显示 SQL 语句 properties: hibernate: format_sql: true # 格式化输出的 SQL jackson: time-zone: GMT+8 # 处理 JSON 时间序列化时的时区问题 date-format: yyyy-MM-dd HH:mm:ss # 自定义配置,如 JWT 密钥(生产环境应从环境变量或配置中心读取) jwt: secret: your-jwt-secret-key-here-must-be-very-long-and-secure expiration: 86400000 # token 过期时间(毫秒),例如 24 小时

关键配置解释:

  • spring.datasource.url: 指定数据库连接地址和参数。serverTimezone=Asia/Shanghai是解决中文时区问题的关键。
  • spring.jpa.hibernate.ddl-auto:update模式会在应用启动时根据实体类自动更新表结构,适合开发。生产环境必须禁用此功能,应使用validate仅校验,或通过 Flyway/Liquibase 管理数据库变更。
  • spring.jpa.show-sql: 开发时开启,便于调试 SQL。
  • jwt.secret: 用于生成和验证 JWT Token 的密钥,必须足够复杂且保密。

2.3 数据库设计与实体类映射

假设系统需要用户表、客房表、订单表。以下以Room(客房)实体为例:

// src/main/java/com/example/hotelmanagement/entity/Room.java package com.example.hotelmanagement.entity; import lombok.Data; import javax.persistence.*; import java.math.BigDecimal; @Entity @Table(name = "room") @Data // Lombok 注解,自动生成 getter, setter, toString, equals, hashCode public class Room { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, unique = true) private String roomNumber; // 房号,如 101 @Column(nullable = false) private String type; // 房型,如 “标准间”、“豪华套房” @Column(nullable = false) private BigDecimal price; // 价格,使用 BigDecimal 避免精度丢失 @Column(nullable = false) private String status; // 状态,如 “空闲”、“已预订”、“入住中”、“维修中” private String description; // 客房描述 private String imageUrl; // 客房图片链接 // 省略构造方法,Lombok @Data 会生成 }

关键点:

  • @Entity和@Table将类映射到数据库表。
  • @Id和@GeneratedValue定义主键及其生成策略。
  • @Column定义字段属性,如是否可为空、是否唯一。
  • 金额字段使用BigDecimal,这是金融计算的最佳实践,避免使用float或double导致的精度问题。
  • 使用Lombok的@Data注解可以极大减少样板代码。

2.4 实现数据访问层与业务逻辑层

1. 仓库接口 (Repository): Spring Data JPA 只需定义接口,无需实现。

// src/main/java/com/example/hotelmanagement/repository/RoomRepository.java package com.example.hotelmanagement.repository; import com.example.hotelmanagement.entity.Room; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import java.util.List; @Repository public interface RoomRepository extends JpaRepository<Room, Long> { // 根据房号查询 Room findByRoomNumber(String roomNumber); // 根据状态查询客房列表 List<Room> findByStatus(String status); // 根据房型和状态查询 List<Room> findByTypeAndStatus(String type, String status); }

2. 服务层 (Service): 服务层封装业务逻辑,是系统的核心。

// src/main/java/com/example/hotelmanagement/service/RoomService.java package com.example.hotelmanagement.service; import com.example.hotelmanagement.entity.Room; import com.example.hotelmanagement.repository.RoomRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.Optional; @Service public class RoomService { @Autowired private RoomRepository roomRepository; public List<Room> findAll() { return roomRepository.findAll(); } public Optional<Room> findById(Long id) { return roomRepository.findById(id); } @Transactional // 确保保存操作在事务中执行 public Room saveOrUpdate(Room room) { // 这里可以加入业务校验,例如房号是否已存在 if (room.getId() == null) { Room existing = roomRepository.findByRoomNumber(room.getRoomNumber()); if (existing != null) { throw new RuntimeException("房号 " + room.getRoomNumber() + " 已存在"); } } return roomRepository.save(room); } @Transactional public void deleteById(Long id) { // 删除前可检查是否有关联订单等业务规则 roomRepository.deleteById(id); } public List<Room> findAvailableRooms() { return roomRepository.findByStatus("空闲"); } }

3. 控制层 (Controller): 控制层负责接收 HTTP 请求,调用服务,并返回 JSON 响应。

// src/main/java/com/example/hotelmanagement/controller/RoomController.java package com.example.hotelmanagement.controller; import com.example.hotelmanagement.entity.Room; import com.example.hotelmanagement.service.RoomService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import java.util.List; @RestController @RequestMapping("/api/rooms") // API 路径前缀 public class RoomController { @Autowired private RoomService roomService; @GetMapping public ResponseEntity<List<Room>> getAllRooms() { return ResponseEntity.ok(roomService.findAll()); } @GetMapping("/{id}") public ResponseEntity<Room> getRoomById(@PathVariable Long id) { return roomService.findById(id) .map(ResponseEntity::ok) .orElse(ResponseEntity.notFound().build()); } @PostMapping public ResponseEntity<Room> createRoom(@Valid @RequestBody Room room) { // @Valid 会触发实体类上的校验注解(如@NotBlank) Room savedRoom = roomService.saveOrUpdate(room); return ResponseEntity.ok(savedRoom); } @PutMapping("/{id}") public ResponseEntity<Room> updateRoom(@PathVariable Long id, @Valid @RequestBody Room room) { if (!roomService.findById(id).isPresent()) { return ResponseEntity.notFound().build(); } room.setId(id); // 确保更新的是指定ID的对象 Room updatedRoom = roomService.saveOrUpdate(room); return ResponseEntity.ok(updatedRoom); } @DeleteMapping("/{id}") public ResponseEntity<Void> deleteRoom(@PathVariable Long id) { if (!roomService.findById(id).isPresent()) { return ResponseEntity.notFound().build(); } roomService.deleteById(id); return ResponseEntity.noContent().build(); } @GetMapping("/available") public ResponseEntity<List<Room>> getAvailableRooms() { return ResponseEntity.ok(roomService.findAvailableRooms()); } }

至此,一个具备 CRUD 功能的客房管理后端 API 就完成了。你可以通过http://localhost:8080/api/rooms来测试接口。

3. 前端 Vue 3 项目搭建与页面开发

后端 API 就绪后,我们开始构建前端用户界面。

3.1 使用 Vite 创建 Vue 3 项目

Vite 是现在 Vue 项目推荐的构建工具,速度远快于传统的 Webpack。

# 在合适目录下执行 npm create vue@latest hotel-management-frontend

按照提示选择项目特性:推荐添加TypeScript、Vue Router、Pinia、ESLint。 创建完成后,进入项目并安装依赖:

cd hotel-management-frontend npm install # 安装 UI 库和 HTTP 客户端 npm install element-plus axios npm install --save-dev @types/node # 解决 TypeScript 路径别名问题

3.2 配置 Axios 与 API 封装

为了统一管理 API 请求,我们创建一个axios实例并封装请求拦截器(用于添加 Token)和响应拦截器(用于统一处理错误)。

// src/utils/request.ts import axios from 'axios'; import { ElMessage } from 'element-plus'; import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'; import { useUserStore } from '@/stores/user'; // 假设使用 Pinia 管理用户状态 const service: AxiosInstance = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:8080/api', // 从环境变量读取 timeout: 10000, // 请求超时时间 }); // 请求拦截器 service.interceptors.request.use( (config: AxiosRequestConfig) => { const userStore = useUserStore(); if (userStore.token) { // 如果存在 token,则在请求头中携带 config.headers = config.headers || {}; config.headers['Authorization'] = `Bearer ${userStore.token}`; } return config; }, (error) => { console.error('Request Error:', error); return Promise.reject(error); } ); // 响应拦截器 service.interceptors.response.use( (response: AxiosResponse) => { const res = response.data; // 这里根据后端统一的响应格式进行调整,例如 { code: 200, data: {}, message: 'success' } if (res.code && res.code !== 200) { ElMessage.error(res.message || 'Error'); return Promise.reject(new Error(res.message || 'Error')); } return res; // 直接返回 data 部分 }, (error) => { console.error('Response Error:', error); if (error.response) { // 有响应但状态码不在 2xx 范围 switch (error.response.status) { case 401: ElMessage.error('未授权,请重新登录'); // 清除 token 并跳转到登录页 const userStore = useUserStore(); userStore.logout(); window.location.href = '/login'; break; case 403: ElMessage.error('拒绝访问'); break; case 404: ElMessage.error('请求的资源不存在'); break; case 500: ElMessage.error('服务器内部错误'); break; default: ElMessage.error(error.response.data?.message || `请求失败: ${error.response.status}`); } } else if (error.request) { // 请求已发出但没有收到响应 ElMessage.error('网络错误,请检查网络连接'); } else { // 请求配置出错 ElMessage.error('请求配置错误'); } return Promise.reject(error); } ); export default service;

然后在.env.development和.env.production中配置后端 API 地址:

# .env.development VITE_API_BASE_URL=http://localhost:8080/api

3.3 实现客房列表页面

使用 Element Plus 的表格组件来展示客房列表。

<!-- src/views/RoomListView.vue --> <template> <div class="room-list"> <div class="header"> <h2>客房管理</h2> <el-button type="primary" @click="handleCreate">新增客房</el-button> </div> <el-table :data="roomList" border style="width: 100%"> <el-table-column prop="roomNumber" label="房号" width="120" /> <el-table-column prop="type" label="房型" width="150" /> <el-table-column prop="price" label="价格(元)" width="120"> <template #default="scope"> {{ scope.row.price.toFixed(2) }} </template> </el-table-column> <el-table-column prop="status" label="状态" width="100"> <template #default="scope"> <el-tag :type="getStatusTagType(scope.row.status)"> {{ scope.row.status }} </el-tag> </template> </el-table-column> <el-table-column prop="description" label="描述" /> <el-table-column label="操作" width="200" fixed="right"> <template #default="scope"> <el-button size="small" @click="handleEdit(scope.row)">编辑</el-button> <el-button size="small" type="danger" @click="handleDelete(scope.row.id)">删除</el-button> </template> </el-table-column> </el-table> <!-- 新增/编辑对话框 --> <el-dialog v-model="dialogVisible" :title="dialogTitle" width="500px"> <el-form :model="form" :rules="rules" ref="formRef" label-width="80px"> <el-form-item label="房号" prop="roomNumber"> <el-input v-model="form.roomNumber" placeholder="请输入房号" /> </el-form-item> <el-form-item label="房型" prop="type"> <el-select v-model="form.type" placeholder="请选择房型"> <el-option label="标准间" value="标准间" /> <el-option label="大床房" value="大床房" /> <el-option label="豪华套房" value="豪华套房" /> </el-select> </el-form-item> <el-form-item label="价格" prop="price"> <el-input-number v-model="form.price" :min="0" :precision="2" :step="10" /> </el-form-item> <el-form-item label="状态" prop="status"> <el-select v-model="form.status" placeholder="请选择状态"> <el-option label="空闲" value="空闲" /> <el-option label="已预订" value="已预订" /> <el-option label="入住中" value="入住中" /> <el-option label="维修中" value="维修中" /> </el-select> </el-form-item> <el-form-item label="描述" prop="description"> <el-input v-model="form.description" type="textarea" :rows="3" /> </el-form-item> </el-form> <template #footer> <span class="dialog-footer"> <el-button @click="dialogVisible = false">取消</el-button> <el-button type="primary" @click="submitForm">确认</el-button> </span> </template> </el-dialog> </div> </template> <script setup lang="ts"> import { ref, reactive, onMounted } from 'vue'; import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'; import request from '@/utils/request'; import type { Room } from '@/types/room'; // 定义好的 TypeScript 类型 const roomList = ref<Room[]>([]); const dialogVisible = ref(false); const dialogTitle = ref(''); const formRef = ref<FormInstance>(); const form = reactive<Partial<Room>>({ roomNumber: '', type: '', price: 0, status: '空闲', description: '', }); const rules: FormRules = { roomNumber: [{ required: true, message: '请输入房号', trigger: 'blur' }], type: [{ required: true, message: '请选择房型', trigger: 'change' }], price: [{ required: true, message: '请输入价格', trigger: 'blur' }], status: [{ required: true, message: '请选择状态', trigger: 'change' }], }; // 获取客房列表 const fetchRooms = async () => { try { const response = await request.get('/rooms'); roomList.value = response.data || response; // 根据后端返回格式调整 } catch (error) { console.error('获取客房列表失败:', error); } }; // 状态标签样式 const getStatusTagType = (status: string) => { const map: Record<string, 'success' | 'info' | 'warning' | 'danger'> = { '空闲': 'success', '已预订': 'warning', '入住中': 'info', '维修中': 'danger', }; return map[status] || 'info'; }; // 打开新增对话框 const handleCreate = () => { dialogTitle.value = '新增客房'; Object.keys(form).forEach(key => { (form as any)[key] = ''; }); form.status = '空闲'; form.price = 0; dialogVisible.value = true; }; // 打开编辑对话框 const handleEdit = (row: Room) => { dialogTitle.value = '编辑客房'; Object.assign(form, row); dialogVisible.value = true; }; // 提交表单(新增/编辑) const submitForm = async () => { if (!formRef.value) return; await formRef.value.validate(async (valid) => { if (valid) { try { const url = form.id ? `/rooms/${form.id}` : '/rooms'; const method = form.id ? 'put' : 'post'; await request({ url, method, data: form }); ElMessage.success(form.id ? '更新成功' : '创建成功'); dialogVisible.value = false; fetchRooms(); // 刷新列表 } catch (error) { console.error('操作失败:', error); } } }); }; // 删除客房 const handleDelete = (id: number) => { ElMessageBox.confirm('确定要删除这条客房信息吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', }).then(async () => { try { await request.delete(`/rooms/${id}`); ElMessage.success('删除成功'); fetchRooms(); } catch (error) { console.error('删除失败:', error); } }).catch(() => {}); }; onMounted(() => { fetchRooms(); }); </script> <style scoped> .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; } </style>

这个页面实现了客房的增删改查功能,并与后端 API 进行了对接。通过类似的模式,可以继续开发用户管理、订单管理等其他页面。

4. 前后端联调与项目运行

代码编写完成后,最关键的一步是让前后端协同工作。

4.1 启动后端服务

  1. 确保 MySQL 服务已启动,并创建了配置文件中的数据库(如hotel_db)。
  2. 在 IDE 中找到HotelManagementApplication主类,直接运行main方法。
  3. 观察控制台日志,确认没有错误,并看到类似Tomcat started on port(s): 8080的日志。
  4. 打开浏览器,访问http://localhost:8080/api/rooms,如果返回空数组[]或提示Whitelabel Error Page(因为没数据),说明后端 API 服务已正常启动。可以通过 Postman 等工具测试 POST、PUT 等接口。

4.2 启动前端开发服务器

  1. 在终端中进入前端项目根目录 (hotel-management-frontend)。
  2. 运行npm run dev。
  3. 控制台会输出本地开发服务器地址,通常是http://localhost:5173。
  4. 打开该地址,应该能看到 Vue 应用的默认页面。

4.3 解决跨域问题

在开发阶段,前端运行在localhost:5173,后端运行在localhost:8080,端口不同,浏览器会因同源策略而阻止请求。有两种主流解决方案:

方案一:后端配置 CORS(推荐)在 Spring Boot 后端添加一个全局 CORS 配置类。

// src/main/java/com/example/hotelmanagement/config/WebConfig.java package com.example.hotelmanagement.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig { @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") // 允许跨域的路径 .allowedOrigins("http://localhost:5173") // 允许的前端地址 .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允许的请求方法 .allowedHeaders("*") // 允许的请求头 .allowCredentials(true); // 允许携带凭证(如 Cookie) } }; } }

方案二:前端代理(Vite 配置)在前端项目的vite.config.ts中配置代理,将 API 请求转发到后端。

// vite.config.ts import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], server: { proxy: { '/api': { target: 'http://localhost:8080', // 后端地址 changeOrigin: true, // rewrite: (path) => path.replace(/^\/api/, '') // 如果后端路径没有 /api 前缀,可以重写 } } } })

配置后,前端代码中请求/api/rooms会被 Vite 开发服务器代理到http://localhost:8080/api/rooms,从而避免跨域。

注意:方案一更接近生产环境,因为生产环境前后端可能部署在同一域名下或由 Nginx 反向代理。方案二仅用于开发环境。

4.4 功能验证流程

  1. 登录/认证:如果系统有登录功能,先完成登录流程,确保 Token 能被正确存储和携带。
  2. 数据列表:访问客房列表页,确认能成功从后端获取并展示数据。
  3. 数据操作:尝试新增、编辑、删除一条客房记录,观察前端提示和后端数据库变化是否一致。
  4. 表单验证:测试表单的必填项、格式校验(如价格不能为负数)是否生效。
  5. 错误处理:尝试触发一些错误(如删除不存在的记录),查看前端拦截器是否能正确捕获并显示错误信息。

5. 常见问题排查与解决方案

在搭建和运行过程中,你可能会遇到以下典型问题。

5.1 后端启动失败

问题现象可能原因检查与解决
APPLICATION FAILED TO START1. 数据库连接失败。
2. 端口被占用。
3. 依赖冲突或缺失。
1. 检查application.yml中的数据库 URL、用户名、密码,确认 MySQL 服务已启动。
2. 检查 8080 端口是否被其他程序占用,可修改server.port。
3. 运行mvn clean compile检查依赖,或尝试删除本地 Maven 仓库(~/.m2/repository)重新下载。
BeanCreationException1. Bean 注入失败。
2. 配置类扫描路径问题。
1. 检查@Service,@Repository,@Autowired注解使用是否正确,包路径是否在@SpringBootApplication主类所在包或其子包下。
2. 确认@ComponentScan注解(如有)配置正确。
JPA相关错误1. 实体类与数据库表映射错误。
2.ddl-auto策略导致。
1. 检查实体类字段名、类型与数据库表是否匹配。
2. 开发环境可暂时设为create-drop或update,但生产环境必须改为validate或none,并通过 SQL 脚本管理表结构。

5.2 前端编译或运行错误

问题现象可能原因检查与解决
npm install失败1. 网络问题。
2. Node.js 版本不兼容。
3.package-lock.json冲突。
1. 切换 npm 镜像源:npm config set registry https://registry.npmmirror.com。
2. 使用nvm管理 Node.js 版本,确保版本符合要求。
3. 删除node_modules和package-lock.json,重新执行npm install。
Vue组件无法导入1. 路径别名@未配置。
2. TypeScript 类型声明缺失。
1. 检查vite.config.ts和tsconfig.json中的paths配置。
2. 为第三方库安装类型声明包,如@types/node。
页面空白或控制台报4041. 路由配置错误。
2. 静态资源路径错误。
3. 代理配置未生效。
1. 检查router/index.ts中的路由定义和组件导入。
2. 使用import.meta.env.BASE_URL处理基础路径。
3. 确认vite.config.ts中的代理配置正确,并重启开发服务器。

5.3 前后端联调问题

问题现象可能原因检查与解决
前端请求后端返回4041. 后端 API 路径错误。
2. 后端 Controller 未正确映射。
3. 跨域请求被浏览器拦截。
1. 核对前端请求的 URL 和后端@RequestMapping定义的路径是否完全一致(包括大小写)。
2. 在后端控制台查看映射日志,或访问/actuator/mappings(需引入 actuator 依赖)查看所有端点。
3. 按4.3节配置 CORS 或代理。
前端请求返回401/4031. 未登录或 Token 过期。
2. Token 未正确携带或格式错误。
3. 后端权限拦截器配置问题。
1. 检查登录流程,确保成功获取 Token 并存储(如 localStorage 或 Pinia)。
2. 检查 Axios 请求拦截器是否正确在请求头中添加了Authorization: Bearer <token>。
3. 检查后端 Security 或自定义拦截器的配置,确保对应接口路径未被错误拦截。
请求成功但数据不显示1. 前端组件数据绑定错误。
2. 后端返回的数据结构与前端期望不符。
3. 控制台有 JavaScript 错误。
1. 使用 Vue Devtools 检查组件的数据和状态。
2. 在浏览器开发者工具的 Network 面板查看 API 返回的实际 JSON 结构,并与前端代码中定义的 TypeScript 接口或变量进行比对。
3. 解决控制台报错,可能是未定义的变量或方法。

6. 项目打包与生产环境部署建议

本地开发完成后,需要将项目部署到服务器。

6.1 后端打包与运行

  1. 打包:在项目根目录执行mvn clean package -DskipTests。成功后会在target目录生成hotel-management-0.0.1-SNAPSHOT.jar。
  2. 运行:将 JAR 包上传至服务器,使用命令运行:
    # 最简单方式(前台运行,关闭终端即停止) java -jar hotel-management-0.0.1-SNAPSHOT.jar # 后台运行,并将日志输出到文件 nohup java -jar hotel-management-0.0.1-SNAPSHOT.jar > app.log 2>&1 & # 指定配置文件(生产环境推荐) java -jar hotel-management-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod
    需要提前在服务器安装好对应版本的 JDK。

6.2 前端打包与部署

  1. 打包:在前端项目根目录执行npm run build。产物会生成在dist目录。
  2. 部署:将dist目录下的所有文件,放到任何静态文件服务器(如 Nginx、Apache)或对象存储(如 OSS)上。
  3. Nginx 配置示例(同时代理前端静态文件和后端 API):
    server { listen 80; server_name your-domain.com; # 你的域名 # 前端静态文件 location / { root /path/to/your/dist; index index.html; try_files $uri $uri/ /index.html; # 支持 Vue Router 的 history 模式 } # 反向代理后端 API location /api/ { proxy_pass http://localhost:8080; # 后端服务地址 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }

6.3 生产环境关键检查清单

将项目部署到生产环境前,务必完成以下检查:

  • 数据库:
    • [ ] 使用独立的、高权限的生产数据库账号,而非root。
    • [ ] 关闭 JPA 的ddl-auto(设为validate或none),使用 Flyway/Liquibase 管理数据库变更。
    • [ ] 对敏感配置(如数据库密码、JWT 密钥)进行加密或从环境变量读取。
  • 应用配置:
    • [ ] 创建application-prod.yml,配置生产环境的数据库、日志级别、端口等。
    • [ ] 确保日志已配置并输出到文件,便于排查问题。
    • [ ] 关闭开发工具(如spring-boot-devtools)和调试信息。
  • 安全:
    • [ ] 实现完整的用户认证与授权(如使用 Spring Security + JWT)。
    • [ ] 对用户输入进行严格的校验和过滤,防止 SQL 注入和 XSS 攻击。
    • [ ] API 接口根据需要实施限流和防刷策略。
  • 运维:
    • [ ] 使用进程管理工具(如 systemd, Supervisor)来管理 JAR 包进程,实现开机自启和自动重启。
    • [ ] 设置监控和告警,关注应用 CPU、内存、磁盘和关键接口响应时间。
    • [ ] 制定数据库备份策略。

通过以上步骤,你不仅能够成功搭建并运行一个 Spring Boot + Vue 3 的酒店客房管理系统,更能掌握全栈开发的核心流程、联调技巧和部署要点。这个项目可以作为深入学习微服务、分布式缓存、消息队列等更高级技术的基础框架。

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

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

立即咨询