ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Spring Boot+Vue 3酒店管理系统实战:从零构建前后端分离项目

Spring Boot+Vue 3酒店管理系统实战:从零构建前后端分离项目 在实际企业级应用开发中酒店管理系统是一个集客房管理、订单处理、客户信息、财务统计于一体的综合性项目。它不仅是常见的毕业设计选题更是检验开发者能否将前后端分离、数据库设计、业务逻辑分层等核心技能融会贯通的实战场景。本文将以一个基于 Spring Boot 后端和 Vue 3 前端的酒店管理系统为例手把手带你从零开始完成一个结构清晰、功能完整、可直接运行的实战项目。你将不仅获得一套可用的源码和数据库更能理解如何将 Spring Boot 的自动配置、MyBatis-Plus 的数据操作、Vue 3 的组合式 API 以及 Element Plus 的组件库串联起来构建一个具备增删改查、权限控制、数据可视化等典型功能的管理后台。文章会详细解释每个模块的设计思路、关键代码的实现逻辑、配置文件的参数含义并提供完整的排错路径和部署建议确保你能在自己的开发环境中成功运行并理解其背后的工程实践。1. 项目整体架构与技术选型解析一个可维护、易扩展的酒店管理系统其技术栈的选型决定了后续开发的效率和项目的稳定性。本项目的核心是前后端分离架构后端提供 RESTful API前端负责页面渲染和用户交互。1.1 后端技术栈Spring Boot 为核心的 Java 生态后端采用 Spring Boot 2.7.x 作为基础框架它极大地简化了 Spring 应用的初始搭建和开发过程。选择此版本是因为其长期支持LTS状态稳定社区资源丰富。持久层使用 MyBatis-Plus 作为 ORM 框架。它是对 MyBatis 的增强在保留 MyBatis 灵活性的同时提供了通用的 CRUD 操作无需编写大量 XML 映射文件能显著提升开发效率。例如对于Room客房表的常规增删改查通过继承BaseMapper即可获得。数据校验集成 Hibernate Validator用于对控制器Controller层接收的参数进行注解式校验确保输入数据的合法性如客房价格不能为负数。权限控制采用 Spring Security 结合 JWTJSON Web Token实现认证与授权。用户登录成功后后端生成一个加密的 Token 返回给前端前端在后续请求中携带此 Token后端通过过滤器链进行校验从而保护 API 接口。数据库使用 MySQL 8.0 作为关系型数据库。其事务支持完善适合处理订单、账单等需要强一致性的业务。项目管理与构建使用 Maven 进行依赖管理和项目构建。1.2 前端技术栈Vue 3 构建现代化管理界面前端采用 Vue 3 的组合式 APIComposition API进行开发相比 Vue 2 的选项式 API逻辑组织和复用性更强。UI 框架使用 Element Plus它是基于 Vue 3 的桌面端组件库提供了丰富、美观的组件如表格、表单、弹窗、导航菜单等能快速搭建出专业的管理后台界面。状态管理使用 Pinia 作为状态管理库。它是 Vue 官方推荐的状态管理工具比 Vuex 更简洁TypeScript 支持更好用于管理用户登录状态、全局配置等跨组件数据。路由管理使用 Vue Router 4.x 进行前端路由控制实现单页面应用SPA的无刷新页面切换。HTTP 客户端使用 Axios 库发起 HTTP 请求与后端 API 进行通信。我们会对其进行封装统一处理请求拦截如添加 Token、响应拦截如处理通用错误和基础配置。构建工具使用 Vite 作为前端构建工具。Vite 提供了极速的冷启动和快速的热更新开发体验远优于传统的 Webpack。1.3 系统核心功能模块设计酒店管理系统的业务模块通常围绕核心实体展开。以下是本项目设计的主要功能模块模块名称核心实体主要功能权限管理User, Role, Menu用户登录、注销、角色分配、菜单权限控制。客房管理Room, RoomType客房的增删改查、状态管理空闲、已预订、入住中、维修中、房型管理。客户管理Customer客户信息的登记、查询、会员管理。订单管理Order客户预订客房、入住登记、退房结账、订单历史查询。财务管理Bill账单生成、支付记录、收入统计。数据统计-基于 ECharts 的可视化图表展示客房入住率、收入趋势等。2. 开发环境准备与项目初始化在开始编码之前必须确保本地开发环境配置正确这是项目能成功运行的第一步。2.1 后端环境准备Java 开发工具包 (JDK)安装 JDK 8 或 JDK 11推荐 JDK 11。配置JAVA_HOME环境变量。# 检查安装是否成功 java -version集成开发环境 (IDE)使用 IntelliJ IDEA社区版或旗舰版或 Eclipse需安装 Spring Tools。IDEA 对 Spring Boot 的支持更友好。数据库安装 MySQL 8.0并启动服务。创建一个名为hotel_management的数据库字符集建议使用utf8mb4。CREATE DATABASE hotel_management DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;项目管理工具安装 Maven 3.6并配置MAVEN_HOME和PATH。# 检查安装是否成功 mvn -v2.2 前端环境准备Node.js安装 Node.js 16 和 npm。建议使用 nvmNode Version Manager管理多版本。# 检查安装是否成功 node -v npm -v包管理工具可以使用 npm但更推荐使用速度更快的pnpm或yarn。# 安装 pnpm npm install -g pnpm pnpm -v2.3 初始化 Spring Boot 后端项目使用 Spring Initializr可通过 IDEA 内置或访问 start.spring.io 生成项目骨架。Project: Maven ProjectLanguage: JavaSpring Boot: 2.7.xProject Metadata:Group:com.exampleArtifact:hotel-backendPackaging: JarDependencies: 添加Spring Web,Spring Security,MyBatis Framework,MySQL Driver,Lombok。生成并导入 IDE 后项目结构大致如下hotel-backend ├── src/main/java/com/example/hotelbackend │ ├── controller/ # 控制器层接收请求返回响应 │ ├── entity/ # 实体类对应数据库表 │ ├── mapper/ # MyBatis Mapper 接口 │ ├── service/ # 业务逻辑层接口 │ │ └── impl/ # 业务逻辑层实现 │ ├── config/ # 配置类如Security, MyBatis-Plus, CORS等 │ └── HotelBackendApplication.java # 主启动类 ├── src/main/resources │ ├── application.yml # 主配置文件 │ └── mapper/ # MyBatis XML映射文件如果使用 └── pom.xml # Maven依赖配置文件2.4 初始化 Vue 3 前端项目使用 Vite 官方模板快速创建 Vue 3 项目。# 使用 npm npm create vuelatest hotel-frontend # 或使用 pnpm pnpm create vuelatest hotel-frontend在创建过程中根据提示选择需要的特性Add TypeScript?Yes(推荐增强类型安全)Add JSX Support?NoAdd Vue Router for Single Page Application?YesAdd Pinia for state management?YesAdd Vitest for Unit Testing?No(根据需求)Add an End-to-End Testing Solution?NoAdd ESLint for code quality?Yes创建完成后进入项目目录并安装 Element Plus 和 Axios。cd hotel-frontend pnpm install element-plus axios # 或使用 npm npm install element-plus axios安装完成后前端项目结构如下hotel-frontend ├── src/ │ ├── api/ # 封装所有后端API请求 │ ├── assets/ # 静态资源 │ ├── components/ # 可复用组件 │ ├── router/ # 路由配置 │ ├── stores/ # Pinia状态管理 │ ├── views/ # 页面视图组件 │ ├── App.vue # 根组件 │ └── main.ts # 入口文件 ├── index.html # HTML模板 ├── package.json # 项目依赖和脚本 └── vite.config.ts # Vite配置3. 后端核心功能实现详解我们将以“客房管理”模块为例贯穿展示从实体设计到接口暴露的全过程。3.1 数据库设计与实体类映射首先在 MySQL 中创建room表。CREATE TABLE room ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键ID, room_number varchar(20) NOT NULL COMMENT 房间号, room_type_id bigint NOT NULL COMMENT 房型ID, status tinyint NOT NULL DEFAULT 0 COMMENT 状态0-空闲1-已预订2-入住中3-维修中, price_per_night decimal(10,2) NOT NULL COMMENT 每晚价格, description varchar(500) DEFAULT NULL COMMENT 房间描述, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_room_number (room_number), KEY idx_room_type (room_type_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci COMMENT客房表;对应的 Java 实体类Room.javapackage com.example.hotelbackend.entity; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import java.math.BigDecimal; import java.time.LocalDateTime; Data TableName(room) // 指定表名 public class Room { TableId(type IdType.AUTO) // 主键自增 private Long id; private String roomNumber; private Long roomTypeId; private Integer status; // 使用枚举更佳此处为简化用Integer private BigDecimal pricePerNight; private String description; TableField(fill FieldFill.INSERT) // 插入时自动填充 private LocalDateTime createTime; TableField(fill FieldFill.INSERT_UPDATE) // 插入和更新时自动填充 private LocalDateTime updateTime; }这里使用了 Lombok 的Data注解自动生成 getter、setter 等方法以及 MyBatis-Plus 的注解进行 ORM 映射。3.2 配置 MyBatis-Plus 与数据源在application.yml中配置数据库连接和 MyBatis-Plus。spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/hotel_management?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: your_password mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印SQL生产环境关闭 global-config: db-config: logic-delete-field: deleted # 全局逻辑删除字段名如果启用 logic-delete-value: 1 logic-not-delete-value: 0 mapper-locations: classpath*:/mapper/**/*.xml # XML映射文件位置创建一个配置类MybatisPlusConfig.java启用 Mapper 接口扫描和分页插件。package com.example.hotelbackend.config; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; Configuration MapperScan(com.example.hotelbackend.mapper) // 扫描Mapper接口 public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); // 添加分页插件 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }3.3 实现 Mapper、Service 和 Controller1. Mapper 层创建RoomMapper.java接口继承 MyBatis-Plus 的BaseMapper。package com.example.hotelbackend.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.hotelbackend.entity.Room; public interface RoomMapper extends BaseMapperRoom { // 可以在此定义自定义的复杂SQL方法 }2. Service 层创建业务接口RoomService.java及其实现类RoomServiceImpl.java。// RoomService.java package com.example.hotelbackend.service; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import com.example.hotelbackend.entity.Room; public interface RoomService extends IServiceRoom { PageRoom getRoomPage(PageRoom page, String keyword); } // RoomServiceImpl.java package com.example.hotelbackend.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.hotelbackend.entity.Room; import com.example.hotelbackend.mapper.RoomMapper; import com.example.hotelbackend.service.RoomService; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; Service public class RoomServiceImpl extends ServiceImplRoomMapper, Room implements RoomService { Override public PageRoom getRoomPage(PageRoom page, String keyword) { LambdaQueryWrapperRoom wrapper new LambdaQueryWrapper(); if (StringUtils.isNotBlank(keyword)) { // 根据房间号或描述模糊查询 wrapper.like(Room::getRoomNumber, keyword) .or() .like(Room::getDescription, keyword); } // 按创建时间倒序 wrapper.orderByDesc(Room::getCreateTime); return baseMapper.selectPage(page, wrapper); } }3. Controller 层创建RoomController.java处理 HTTP 请求。package com.example.hotelbackend.controller; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.example.hotelbackend.common.Result; import com.example.hotelbackend.entity.Room; import com.example.hotelbackend.service.RoomService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; RestController RequestMapping(/api/room) public class RoomController { Autowired private RoomService roomService; // 新增客房 PostMapping public Result addRoom(Valid RequestBody Room room) { // 业务校验如房间号是否重复 boolean saved roomService.save(room); return saved ? Result.success() : Result.error(新增失败); } // 分页查询客房列表 GetMapping(/page) public Result getRoomPage(RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize, RequestParam(required false) String keyword) { PageRoom page new Page(pageNum, pageSize); PageRoom roomPage roomService.getRoomPage(page, keyword); return Result.success(roomPage); } // 根据ID查询客房详情 GetMapping(/{id}) public Result getRoomById(PathVariable Long id) { Room room roomService.getById(id); return room ! null ? Result.success(room) : Result.error(客房不存在); } // 更新客房信息 PutMapping public Result updateRoom(Valid RequestBody Room room) { boolean updated roomService.updateById(room); return updated ? Result.success() : Result.error(更新失败); } // 删除客房逻辑删除 DeleteMapping(/{id}) public Result deleteRoom(PathVariable Long id) { boolean removed roomService.removeById(id); return removed ? Result.success() : Result.error(删除失败); } }这里使用了统一的Result类封装返回结果包含code、message和data字段便于前端处理。3.4 配置 Spring Security 与 JWT为了安全需要配置 Spring Security 和 JWT。首先添加依赖到pom.xml。!-- JWT 支持 -- dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt/artifactId version0.9.1/version /dependency创建 JWT 工具类JwtUtil.java用于生成和解析 Token。然后创建SecurityConfig.java配置类定义密码编码器、认证管理器、安全过滤链等。核心是配置哪些路径需要认证哪些可以匿名访问并添加一个自定义的 JWT 认证过滤器。4. 前端核心功能实现与联调前端将使用 Vue 3 Element Plus 构建客房管理的页面并通过 Axios 与后端 API 通信。4.1 封装 Axios 请求实例在src/api/request.ts中创建 Axios 实例并配置请求/响应拦截器。import axios from axios; import { ElMessage } from element-plus; import router from /router; import { useUserStore } from /stores/user; const request axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL || http://localhost:8080/api, // 从环境变量读取 timeout: 10000, }); // 请求拦截器在请求头中添加 Token request.interceptors.request.use( (config) { const userStore useUserStore(); const token userStore.token; if (token) { config.headers.Authorization Bearer ${token}; } return config; }, (error) { return Promise.reject(error); } ); // 响应拦截器统一处理错误 request.interceptors.response.use( (response) { const res response.data; // 假设后端统一返回 { code: 200, message: success, data: ... } if (res.code 200) { return res.data; } else { ElMessage.error(res.message || 请求失败); return Promise.reject(new Error(res.message || Error)); } }, (error) { if (error.response?.status 401) { ElMessage.warning(登录已过期请重新登录); router.push(/login); } else { ElMessage.error(error.response?.data?.message || error.message || 网络错误); } return Promise.reject(error); } ); export default request;4.2 实现客房管理 API 模块在src/api/room.ts中定义所有与客房相关的 API 函数。import request from ./request; // 定义 Room 类型接口应与后端实体对应 export interface Room { id?: number; roomNumber: string; roomTypeId: number; status: number; pricePerNight: number; description?: string; } // 分页查询参数 export interface RoomPageParams { pageNum: number; pageSize: number; keyword?: string; } // 分页返回结果 export interface PageResultT { records: T[]; total: number; size: number; current: number; } // API 函数 export const roomApi { // 新增客房 addRoom(data: Room) { return request.post(/room, data); }, // 分页查询客房列表 getRoomPage(params: RoomPageParams) { return request.getPageResultRoom(/room/page, { params }); }, // 根据ID查询客房详情 getRoomById(id: number) { return request.getRoom(/room/${id}); }, // 更新客房信息 updateRoom(data: Room) { return request.put(/room, data); }, // 删除客房 deleteRoom(id: number) { return request.delete(/room/${id}); }, };4.3 构建客房列表页面创建src/views/room/RoomList.vue组件使用 Element Plus 的ElTable和ElPagination组件。template div classroom-list-container el-card template #header div classcard-header span客房管理/span el-button typeprimary clickhandleAdd新增客房/el-button /div /template !-- 搜索栏 -- el-form :inlinetrue :modelsearchForm el-form-item label关键词 el-input v-modelsearchForm.keyword placeholder房间号/描述 clearable clearhandleSearch / /el-form-item el-form-item el-button typeprimary clickhandleSearch查询/el-button el-button clickresetSearch重置/el-button /el-form-item /el-form !-- 客房表格 -- el-table :datatableData border stripe v-loadingloading el-table-column proproomNumber label房间号 width120 / el-table-column proproomTypeId label房型 width100 template #defaultscope {{ getRoomTypeName(scope.row.roomTypeId) }} /template /el-table-column el-table-column propstatus label状态 width100 template #defaultscope el-tag :typestatusTagType(scope.row.status) {{ getStatusText(scope.row.status) }} /el-tag /template /el-table-column el-table-column proppricePerNight label价格(元/晚) width120 / el-table-column propdescription label描述 show-overflow-tooltip / el-table-column propcreateTime label创建时间 width180 / el-table-column label操作 width200 fixedright template #defaultscope el-button sizesmall clickhandleEdit(scope.row)编辑/el-button el-button sizesmall typedanger clickhandleDelete(scope.row)删除/el-button /template /el-table-column /el-table !-- 分页 -- div classpagination-wrapper el-pagination v-model:current-pagepageParams.pageNum v-model:page-sizepageParams.pageSize :page-sizes[10, 20, 50, 100] :totaltotal layouttotal, sizes, prev, pager, next, jumper size-changehandleSizeChange current-changehandleCurrentChange / /div /el-card !-- 新增/编辑对话框 -- room-form-dialog v-modeldialogVisible :form-datacurrentRow :is-editisEdit successhandleDialogSuccess / /div /template script setup langts import { ref, reactive, onMounted } from vue; import { ElMessage, ElMessageBox } from element-plus; import { roomApi, type Room, type RoomPageParams } from /api/room; import RoomFormDialog from ./components/RoomFormDialog.vue; // 搜索表单 const searchForm reactive({ keyword: , }); // 分页参数 const pageParams reactiveRoomPageParams({ pageNum: 1, pageSize: 10, }); // 表格数据 const tableData refRoom[]([]); const total ref(0); const loading ref(false); // 对话框相关 const dialogVisible ref(false); const currentRow refPartialRoom({}); const isEdit ref(false); // 获取客房列表 const fetchRoomList async () { loading.value true; try { const params { ...pageParams, ...searchForm }; const result await roomApi.getRoomPage(params); tableData.value result.records; total.value result.total; } catch (error) { console.error(获取客房列表失败:, error); } finally { loading.value false; } }; // 搜索 const handleSearch () { pageParams.pageNum 1; // 重置到第一页 fetchRoomList(); }; // 重置搜索 const resetSearch () { searchForm.keyword ; handleSearch(); }; // 分页大小变化 const handleSizeChange (val: number) { pageParams.pageSize val; fetchRoomList(); }; // 当前页变化 const handleCurrentChange (val: number) { pageParams.pageNum val; fetchRoomList(); }; // 新增 const handleAdd () { currentRow.value {}; isEdit.value false; dialogVisible.value true; }; // 编辑 const handleEdit (row: Room) { currentRow.value { ...row }; isEdit.value true; dialogVisible.value true; }; // 删除 const handleDelete (row: Room) { ElMessageBox.confirm(确定要删除房间 ${row.roomNumber} 吗, 提示, { confirmButtonText: 确定, cancelButtonText: 取消, type: warning, }).then(async () { try { await roomApi.deleteRoom(row.id!); ElMessage.success(删除成功); fetchRoomList(); } catch (error) { ElMessage.error(删除失败); } }); }; // 对话框操作成功回调 const handleDialogSuccess () { dialogVisible.value false; fetchRoomList(); }; // 状态映射可抽离到常量文件 const statusMap { 0: { text: 空闲, type: success }, 1: { text: 已预订, type: warning }, 2: { text: 入住中, type: primary }, 3: { text: 维修中, type: danger }, }; const getStatusText (status: number) statusMap[status as keyof typeof statusMap]?.text || 未知; const statusTagType (status: number) statusMap[status as keyof typeof statusMap]?.type || info; // 模拟获取房型名称实际应从store或api获取 const getRoomTypeName (id: number) 房型${id}; onMounted(() { fetchRoomList(); }); /script style scoped .card-header { display: flex; justify-content: space-between; align-items: center; } .pagination-wrapper { margin-top: 20px; display: flex; justify-content: flex-end; } /style4.4 配置路由与菜单在src/router/index.ts中配置路由将客房管理页面挂载到路由上并在主布局中通过菜单导航访问。5. 项目运行、验证与常见问题排查完成前后端核心代码后需要启动项目并进行功能验证。5.1 启动后端服务确保 MySQL 服务已启动且hotel_management数据库已创建。在application.yml中检查数据库连接信息是否正确。在 IDEA 中找到主启动类HotelBackendApplication.java右键运行。观察控制台日志确认没有启动错误并看到类似Tomcat started on port(s): 8080的日志。5.2 启动前端开发服务器在终端中进入hotel-frontend目录。运行pnpm dev或npm run dev。控制台会输出本地访问地址通常是http://localhost:5173。5.3 功能验证流程访问前端在浏览器打开http://localhost:5173应能看到登录页或主界面。登录系统使用预设的管理员账号需要在后端初始化数据或通过数据库插入登录。导航至客房管理点击侧边栏菜单进入客房管理页面。测试增删改查查询页面加载后应显示客房列表。新增点击“新增客房”填写表单并提交列表应刷新显示新数据。编辑点击某行数据的“编辑”按钮修改信息后提交列表数据应更新。删除点击“删除”按钮确认后该行数据应从列表中消失后端为逻辑删除。检查网络请求打开浏览器开发者工具的“网络(Network)”标签查看每次操作发出的 API 请求和响应确认状态码为 200数据格式正确。5.4 常见问题与排查路径在联调过程中你可能会遇到以下典型问题问题现象可能原因排查步骤解决方案前端访问后端 API 报跨域错误 (CORS)浏览器安全策略阻止了不同源域名、端口、协议的请求。1. 查看浏览器控制台 Network 标签请求状态为CORS error或OPTIONS 403。2. 检查后端是否配置了 CORS。在后端SecurityConfig或单独配置类中添加 CORS 配置允许前端源。前端请求返回 401 (Unauthorized)1. 未登录或 Token 过期。2. Token 未正确携带或格式错误。3. 请求的接口需要特定权限。1. 检查前端请求头Authorization是否包含Bearer token。2. 检查后端 Token 解析是否失败密钥不匹配、过期。3. 查看后端 Security 配置该接口路径是否需要认证。1. 重新登录获取新 Token。2. 检查前后端 JWT 密钥是否一致。3. 在 Security 配置中放行该接口或为用户添加对应权限。前端请求返回 404 (Not Found)1. 后端 API 路径错误。2. 后端 Controller 未正确映射或未启动。1. 核对前端请求 URL 和后端RequestMapping路径。2. 检查后端启动日志确认 Controller 已被扫描加载。1. 修正前端请求 URL 或后端注解路径。2. 确保主启动类包扫描范围能覆盖到 Controller。数据库连接失败1. MySQL 服务未启动。2. 连接 URL、用户名、密码错误。3. 驱动版本不匹配。1. 检查 MySQL 服务状态。2. 核对application.yml中的连接信息。3. 查看启动日志中的具体异常信息。1. 启动 MySQL 服务。2. 修正配置文件。3. 检查pom.xml中 MySQL 驱动版本。MyBatis-Plus 插入/更新时字段为 null1. 实体类字段名与数据库列名未正确映射下划线转驼峰默认开启。2. 字段填充策略未生效。1. 检查TableField注解的value属性是否指定了列名。2. 检查是否实现了MetaObjectHandler来处理字段自动填充。1. 使用TableField(value column_name)明确指定映射。2. 创建配置类实现MetaObjectHandler接口。前端页面样式错乱或 Element Plus 组件未渲染1. Element Plus 未正确引入或注册。2. Vue 组件未正确导入或注册。1. 检查main.ts中是否正确引入了 Element Plus 及其样式。2. 检查浏览器控制台是否有 JS 错误。1. 参照 Element Plus 官方文档在main.ts中完成全局注册或按需导入。2. 根据控制台错误信息修复组件导入问题。6. 项目部署与生产环境考量学习环境运行成功后若想部署到服务器或用于生产演示还需要考虑以下方面。6.1 后端打包与部署打包在项目根目录执行mvn clean package -DskipTests会在target目录生成可执行的 JAR 文件如hotel-backend-0.0.1-SNAPSHOT.jar。环境配置生产环境的数据库密码、JWT 密钥等敏感信息不应写在代码中。应使用application-prod.yml文件并通过--spring.profiles.activeprod启动参数激活。或者使用环境变量、配置中心。启动在服务器上使用nohup java -jar hotel-backend-0.0.1-SNAPSHOT.jar --spring.profiles.activeprod app.log 21 命令后台启动。反向代理通常使用 Nginx 作为反向代理将域名请求转发到后端服务的 8080 端口并处理 SSL 证书HTTPS。6.2 前端打包与部署打包执行pnpm build或npm run build会在dist目录生成静态资源文件。部署将dist目录下的所有文件上传到 Web 服务器如 Nginx、Apache的静态资源目录。Nginx 配置配置 Nginx将前端请求代理到后端 API并处理 History 路由模式。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 模式 } location /api/ { proxy_pass http://localhost:8080/api/; # 代理后端API proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }6.3 生产环境最佳实践建议数据库为生产数据库创建专属用户并授予最小必要权限。定期备份数据库。对核心表如订单、账单建立合适的索引以优化查询性能。安全JWT 密钥使用强随机字符串并定期更换。API 防护对敏感操作如删除、支付增加二次确认或操作日志。输入校验前后端均需进行严格的输入校验和过滤防止 SQL 注入和 XSS 攻击。依赖安全定期使用mvn dependency:check或npm audit检查依赖库的安全漏洞。日志与监控配置日志框架如 Logback将日志按级别输出到文件便于排查问题。集成 Spring Boot Actuator 暴露健康检查端点方便监控应用状态。考虑使用 Prometheus Grafana 进行应用性能监控。性能对频繁查询且变化不频繁的数据如房型、城市列表使用 Redis 缓存。前端对图片等静态资源进行压缩并使用 CDN 加速。通过以上步骤你不仅完成了一个可运行的酒店管理系统更实践了从技术选型、环境搭建、模块开发、前后端联调到部署上线的完整流程。这个项目可以作为你深入理解 Spring Boot 和 Vue 3 全栈开发的坚实基础你可以在此基础上继续扩展功能如集成工作流引擎处理复杂的订单状态流转或加入更精细的权限管理模型如 RBAC使其更贴近真实的商业系统。
返回列表