多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Spring Boot酒店预订系统实战:从数据库设计到部署全解析

Spring Boot酒店预订系统实战:从数据库设计到部署全解析 做酒店预订系统这件事网上类似的源码不少但真正能照着跑起来、讲清楚为什么这么设计的其实不多。前段时间我完整做了一套基于 Spring Boot 的酒店预订系统包括源码、数据库脚本和配套文档前后折腾了大概三周踩了不少文档里根本不会写的坑。这篇就把这套系统的设计思路、数据库表结构、核心流程实现、部署细节和常见问题一次性梳理清楚如果你正好在找项目练手或者需要一套可以做毕设、实训演示的完整项目可以参考我的做法。这套系统的核心价值不是“又一个 CRUD”而是把酒店预订这条业务链路完整落地用户注册登录、浏览房型、选房下单、订单状态流转、后台管理、统计报表全部串起来。对初学者来说它覆盖了 Java Web 开发最常见的技术点分层架构、关联查询、事务控制、权限校验、日期冲突判断、打包部署。对需要交项目的人来说它代码量适中、业务闭环完整拿出来讲的时候有东西可以说。先说明一下我默认的技术栈Spring Boot 2.7.18 MyBatis-Plus MySQL 8.0 Thymeleaf Bootstrap。如果你更习惯 JPA整体思路一样只是持久层写法不同。1. 系统整体设计与需求拆解1.1 为什么自己写一套酒店预订系统市面上现成的酒店管理系统很多一类是 PMSProperty Management System功能非常重包含房态管理、渠道对接、财务对账、门锁对接等等普通开发者很难二次开发另一类是单体演示项目多半只有简单的增删改查订单状态冲突都不处理这类项目拿出去很容易被问倒。自己写一套的好处在于你可以完全控制业务流程和代码复杂度。酒店预订系统不像电商那样需要大量商品库存、购物车、秒杀等复杂场景但又不至于简单到只是一个“房间表 订单表”。它核心要做到几点房间和房型分离同一房型可以有多间房。预订时必须判断日期范围内该房间是否已经被占用。订单状态要有清晰流转不能出现已经退房还能被预订的问题。管理端和用户端权限分离管理员不能走普通用户的接口逻辑。这套需求拆下来难度刚好卡在“练手有余、复杂不足”的位置非常适合作为完整的 Spring Boot 项目来写。1.2 功能模块与角色权限划分我第一版需求梳理时就把功能分成了用户端和管理端两条线。用户端功能注册、登录、退出。查看房型列表支持按入住日期、退房日期、人数筛选。选择房间、填写入住人信息、提交订单。我的订单列表查看订单状态支持取消未入住的订单。入住完成后可对订单进行评价评分。管理端功能管理员登录首页展示订单总数、今日入住、营业额等基础统计。房型管理新增、编辑、上下架房型、设置价格与库存房间数。房态管理查看每个房间状态空闲、已入住、清洁中、维修中。订单管理查看所有订单执行确认入住、办理退房、取消订单等操作。用户管理查看注册用户禁用异常账号。从角色权限角度看我并没有引入 Spring Security 那一套复杂权限模型而是用了一个很实际的做法用户表加 role 字段0 表示普通用户1 表示管理员再写个拦截器在请求进入 Controller 之前校验登录态和管理员身份。这样的设计对单体项目来说够用也容易讲清楚。如果你想把项目做得更“能打”可以在回答项目问题时补充一句如果要上生产建议引入 Spring Security 做基于角色的 URL 权限控制但当前项目用拦截器是为了降低上手门槛。1.3 技术选型逻辑说明技术选型这件事不要为了炫技而选型每选一个框架都要有理由。Spring Boot 是主框架它的自动配置能省掉大量 XML 配置单纯的 SSM 项目写起来太啰嗦而且 Spring Boot 是现在绝大多数中小型项目的事实标准。MyBatis-Plus 不是必须的但我建议用。它提供通用 CRUD 和分页插件酒店管理这类表关系清晰的系统用它能减少一半以上的重复 Mapper 代码。重要的是它不影响你手写复杂 SQL房间占用判断、订单统计这些查询我仍然是手写 XML。存储用 MySQL成本低、资料多、面试也好讲。日期类型我全部用 DATE不用 DATETIME。因为酒店预订是按天算的DATE 类型配合范围判断最直观用 DATETIME 反而容易在跨天边界上出错这个后面细说。前端用 Thymeleaf 做服务端渲染不搞前后端分离。原因很简单项目核心是后端业务链路不是前端工程化。Thymeleaf 加水了 Bootstrap页面足够干净而且部署时只打一个 Jar 包演示和交付都省事。2. 数据库设计一切业务的地基2.1 核心表结构数据库设计是整套系统里我花时间最多的地方。表结构如果设计错了后面写代码会越写越难受。我一共设计了六张核心表另外加一张操作日志表。用户表t_user字段类型说明idbigint主键自增usernamevarchar(50)用户名唯一passwordvarchar(100)BCrypt 加密后的密码nicknamevarchar(50)昵称phonevarchar(20)手机号id_cardvarchar(18)身份证号roletinyint0 普通用户1 管理员statustinyint0 正常1 禁用create_timedatetime注册时间房型表t_room_type字段类型说明idbigint主键type_namevarchar(50)房型名称如豪华大床房pricedecimal(10,2)每晚参考价bed_numint床位数areavarchar(20)面积如 35㎡imagevarchar(255)房型图片路径descriptionvarchar(500)房型描述statustinyint0 上架1 下架房间表t_room字段类型说明idbigint主键room_type_idbigint关联房型room_novarchar(10)房间号如 501floorint楼层statustinyint0 空闲1 入住2 清洁3 维修订单表t_order字段类型说明idbigint主键order_novarchar(32)订单编号user_idbigint下单用户room_type_idbigint下单时选中的房型room_idbigint实际分配的房间check_in_datedate入住日期check_out_datedate退房日期total_pricedecimal(10,2)总价statustinyint状态字典见下方link_namevarchar(50)入住人姓名link_phonevarchar(20)入住人电话remarkvarchar(255)用户备注create_timedatetime下单时间订单状态我用了数字字典0 待支付、1 已支付、2 已入住、3 已完成、4 已取消。这个字典要写进项目文档里不然看代码的人会一头雾水。评价表t_comment字段类型说明idbigint主键order_idbigint关联订单user_idbigint评价用户scoretinyint1~5 分contentvarchar(500)内容create_timedatetime评价时间表关系其实很简单房型对房间是一对多用户对订单是一对多房间对订单是一对多订单对评价是一对一。我画数据库模型时就用一张简单的 ER 图表达清楚项目文档里也是这么展示的。2.2 预订日期冲突判断的设计细节这一节是整个系统最容易出错的地方。酒店房间在某一段日期内被占用那么新订单必须避开这段时间。用 SQL 怎么写假设新订单的入住日期是newCheckIn退房日期是newCheckOut房间roomId那么冲突条件是SELECT COUNT(*) FROM t_order WHERE room_id #{roomId} AND status IN (0, 1, 2) AND check_in_date #{newCheckOut} AND check_out_date #{newCheckIn}这个判断的逻辑是已有订单的入住时间早于新订单的退房时间并且已有订单的退房时间晚于新订单的入住时间两条订单就一定有日期重叠。没有包含等号是因为退房当天是释放房态的时间允许下一个客人当天入住。很多初学者会写成check_in_date BETWEEN newCheckIn AND newCheckOut这只能判断入住日期落在范围内完全没有覆盖“客人已经在住新订单中途插进来”的情况。这是一种典型的区间重叠判断电商预售、会议室预订、活动场地预订全是一个道理。为了避免时间带小时导致判断错误订单表里入住和退房字段直接用 DATE前端传值也必须传yyyy-MM-dd。我在 DTO 上用DateTimeFormat(pattern yyyy-MM-dd)做注解时间转进数据库后只保留日期彻底杜绝“退房当天凌晨零点多一秒”这种诡异问题。2.3 初始化脚本和数据准备数据库脚本文件我命名为hotel.sql一个文件里包含建库、建表、初始化数据三部分。这样做交付最方便拿到项目的人直接执行一份 SQL 就能把环境跑起来。初始化数据至少要准备这些管理员账号 admin密码用 BCrypt 加密后的密文。这里强调一下不要在 SQL 里直接插入明文密码即使只是演示项目这种坏习惯也不好。可以用一个测试类或者启动时 CommandLineRunner 生成密文后放进脚本。5~8 个常用房型比如单人标准间 188 一晚、豪华大床房 328 一晚、家庭套房 588 一晚。每个房型配 3~6 个实际房间房间号要有规律比如 501、502方便演示时讲解。几条示例订单要刻意做一条“日期跨当前时间”的订单这样首页统计和后台订单列表演示时不必等新用户下单就有数据。初始化脚本里还要注意 MySQL 编码问题建议建库语句带上DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci否则后面写入中文评论、用户昵称时可能出现乱码。3. 核心功能实现与代码结构3.1 工程目录怎么组织我用的是标准 Maven 单模块结构分包方式遵循 Controller-Service-Mapper 三层src/main/java/com/example/hotel/ ├── common/ # 统一返回结果、异常处理、常量定义 ├── config/ # 拦截器、MyBatis-Plus 分页插件配置 ├── controller/ # 前端页面接口和管理端接口 ├── entity/ # 数据库实体 ├── mapper/ # MyBatis-Plus 的 Mapper 接口 ├── service/ # 业务逻辑接口和实现类 └── util/ src/main/resources/ ├── mapper/ # 手写 MyBatis XML ├── static/ # CSS/JS/图片 ├── templates/ # Thymeleaf 页面 └── application.ymlcommon 包里我放了一个ResultT类所有接口统一返回它包含 code、message、data 三个字段。这样做的好处是测试接口时看结构一目了然前端也能用统一逻辑处理成功失败。配置类里我只做了两件额外的事注册 MyBatis-Plus 分页插件注册一个拦截器做登录校验。我的登录状态是存在 Session 里的拦截器里判断 Session 有没有登录用户如果是管理端 URL 前缀/admin/**还要额外判断 role 是否为 1。3.2 实体与通用 CRUD实体类我以TUser、TRoomType、TRoom、TOrder命名避免 order 这个单词和 SQL 关键字冲突。实体注解用 MyBatis-Plus 的TableName和TableId示例TableName(t_order) public class TOrder { TableId(type IdType.AUTO) private Long id; private String orderNo; private Long userId; private Long roomTypeId; private Long roomId; private LocalDate checkInDate; private LocalDate checkOutDate; private BigDecimal totalPrice; private Integer status; // 其余字段与 getter/setter 略 }实体类我建议所有用包装类不要用基本类型否则查询结果为 null 时反序列化会出问题。另外不要偷懒不加注释实体、字段注释写得明白生成数据库文档时能省很多时间。通用 CRUD 用 MyBatis-Plus 就直接继承BaseMapperT不需要写任何 XML。房型新增、用户列表、评价列表等都是这类简单查询代码量可以压缩非常多。3.3 预订下单的业务逻辑与事务控制预订下单是整套系统最核心的方法代码可能不复杂但业务判断顺序一定要对。我实现的逻辑是这样的校验用户登录态。接收入住日期、退房日期、房型 ID、入住人信息。判断日期合法退房日期大于入住日期且入住日期不能早于今天。查询该房型下所有空闲房间。逐个房间做日期冲突判断选第一个没被占用的房间。计算总价房型价格price * (退房日期-入住日期)。生成订单号保存订单状态为待支付。方法整体加Transactional(rollbackFor Exception.class)注解。只要中间任何一步出问题整个订单创建过程回滚不会出现订单没保存成功但房间被标记成不可用的状态。下单核心代码思路如下Transactional(rollbackFor Exception.class) public ResultLong createOrder(OrderCreateDTO dto) { // 日期校验 if (!dto.getCheckOutDate().isAfter(dto.getCheckInDate())) { return Result.error(退房日期必须晚于入住日期); } // 查询房型 TRoomType roomType roomTypeMapper.selectById(dto.getRoomTypeId()); if (roomType null || roomType.getStatus() 1) { return Result.error(房型不存在或已下架); } // 查询该房型所有空闲房间 ListTRoom roomList roomMapper.selectList( new LambdaQueryWrapperTRoom() .eq(TRoom::getRoomTypeId, dto.getRoomTypeId()) .eq(TRoom::getStatus, 0) ); // 对每个房间做日期冲突判断 TRoom selectedRoom null; for (TRoom room : roomList) { int conflictCount orderMapper.countConflictOrders( room.getId(), dto.getCheckInDate(), dto.getCheckOutDate()); if (conflictCount 0) { selectedRoom room; break; } } if (selectedRoom null) { return Result.error(该时段没有可预订房间); } // 计算价格并保存订单 long days ChronoUnit.DAYS.between(dto.getCheckInDate(), dto.getCheckOutDate()); BigDecimal totalPrice roomType.getPrice().multiply(BigDecimal.valueOf(days)); TOrder order new TOrder(); order.setOrderNo(generateOrderNo()); order.setUserId(dto.getUserId()); order.setRoomTypeId(roomType.getId()); order.setRoomId(selectedRoom.getId()); order.setCheckInDate(dto.getCheckInDate()); order.setCheckOutDate(dto.getCheckOutDate()); order.setTotalPrice(totalPrice); order.setStatus(0); order.setLinkName(dto.getLinkName()); order.setLinkPhone(dto.getLinkPhone()); order.setRemark(dto.getRemark()); order.setCreateTime(LocalDateTime.now()); orderMapper.insert(order); return Result.success(order.getId()); }这里面有个并发问题必须提如果两个用户同时看中了同一间房都通过了冲突判断同时执行 insert那就可能出现两笔订单共用同一房间的情况。我的做法是在订单表加一个唯一约束比如(room_id, check_in_date, check_out_date)的组合唯一索引一旦后插入的人违反唯一约束就捕获异常回滚提示“手慢了房间被订走”。对于课程设计和实训项目来说这个方案简单且足够说明你有并发意识。更好的方案是用悲观锁在查询可用房间时执行SELECT ... FOR UPDATE锁住房间行但那样写起来复杂度会高一些且需要事务内保持连接对新手不如唯一约束直观。订单号生成也有讲究。不要用自增 ID 当订单号既不安全也容易被看出业务量。我用的是时间戳加三位随机数yyyyMMddHHmmss 100~999。项目里如果未来要接支付还可以把支付单号也做成一个单独的表现在保持简单即可。3.4 订单状态流转与后台管理订单状态流转不是无脑改字段每一步都要有业务前提。我设置的状态操作用户点击“取消订单”只有待支付和已支付状态能取消已入住的不允许取消。管理员点击“确认入住”订单必须处于已支付状态确认后状态变成已入住同时对应房间 state 改成 1。管理员点击“退房结账”订单必须处于已入住状态退房后订单状态变成已完成房间状态改成 2表示需要清洁。管理员在房间管理里把清洁状态改成空闲后房间才能被新订单查到。所以房间 state 和订单 state 是有联动关系的这个联动逻辑是项目里最容易讲业务深度的点。我当时在 Service 层专门写了checkInOrder和checkOutOrder两个方法状态判断放在同一个事务里避免出现“订单已退房但房间还是入住中”的数据不一致。后台首页统计我做了三个指标累计订单数、今日新订单数、本月营业额。SQL 都不复杂主要是聚合函数和日期范围过滤。营业额统计要排除已取消订单这个规则想清楚就行。用 MyBatis-Plus 写可能啰嗦我直接手写 XMLselect idsumMonthIncome resultTypejava.math.BigDecimal SELECT IFNULL(SUM(total_price), 0) FROM t_order WHERE status IN (1, 2, 3) AND create_time #{monthStart} /select这里的IFNULL很关键否则当月没有订单时返回 nullJava 端处理起来要多写一个判空。4. 前端页面与交互流程4.1 Thymeleaf 页面怎么组织我这个项目前端页面不多但每张页面都有自己的职责。用户端我做了首页、房型列表、房型详情、下单页、我的订单、订单详情。管理端做了登录页、后台主页、房型管理、房态管理、订单管理、用户管理。Thymeleaf 模板放在templates目录下公共部分我用th:fragment抽了头部和底部页面里通过th:replace引用避免每个页面重复写导航栏。模板引擎的好处是直接在 HTML 里写th:each循环输出数据不需要像前后端分离那样额外调接口。比如房型列表的核心代码div classrow th:eachroomType : ${roomTypeList} div classcol-md-4 h4 th:text${roomType.typeName}房型名/h4 p th:text${¥ roomType.price /晚}价格/p a th:href{/room/detail/ ${roomType.id}}查看详情/a /div /div前端页面有一个必须处理的细节日期选择。我用的组件是 Bootstrap 风格的日期控件限制用户不能选择今天之前的日期退房日期必须晚于入住日期。这些规则虽然后端也会校验但前端先拦截一次用户体验会好很多。4.2 预订表单和筛选条件的实现房型列表页上方放了一个搜索区域用户填写入住日期、退房日期和入住人数后提交 GET 请求到/room/search。Controller 里接收三个参数传给 Service 查出满足条件的房型。这里的“满足条件”不是只看房型有没有房而是要按日期实时判断。我实现方式是先把该房型下所有房间查出来再统计每个房型在目标日期段内可订房间数量数量大于 0 才展示。这个查询逻辑如果全在 SQL 里写会比较绕我采用“先粗筛再精确判断”的思路先按人数和上架状态筛出部分房型再逐个房型统计剩余可订房间。统计可订房间数量的 SQL 可以这样写SELECT COUNT(*) FROM t_room r WHERE r.room_type_id #{roomTypeId} AND r.status 0 AND r.id NOT IN ( SELECT o.room_id FROM t_order o WHERE o.room_type_id #{roomTypeId} AND o.status IN (0, 1, 2) AND o.check_in_date #{checkOutDate} AND o.check_out_date #{checkInDate} )这个 SQL 的语义是空闲房间总数减去已经被订单占用的房间剩下的就是可订房间。日期重叠条件和我们之前说的一致。下单页我放了一个隐藏字段记录房型 ID用户填写入住人姓名、电话确认总价后提交 POST 请求。页面上的总价我用 JavaScript 根据选中的日期实时计算后端在保存订单时还会再算一次前端算给你的只是参考后端算的才是最终落库数据。4.3 管理后台的交互要点管理后台的交互要重点关注状态流转按钮的显示条件。比如订单管理列表中每一行订单根据当前状态展示不同的操作按钮待支付显示“取消”已支付显示“确认入住”已入住显示“办理退房”已完成就不显示操作按钮。Thymeleaf 里可以用th:if做条件判断比如button th:if${order.status 1} classbtn btn-success th:data-id${order.id} onclickconfirmCheckIn(this)确认入住/button这些按钮点击后是走的异步请求用 JavaScript 的 fetch 提交到管理端接口成功后再刷新当前页面。我没做很复杂的弹窗全部用浏览器的确认弹窗简单直接。后台房态管理页面我用一个网格展示所有房间每个格子显示房间号和当前状态颜色区分绿色空闲、红色入住、黄色清洁、灰色维修。做出来效果很直观演示时也容易给看的人留下好印象。5. 部署步骤与文档整理5.1 本地开发环境准备先把环境列清楚缺了哪个后面都会卡住JDK 8 或 11我用的是 JDK 8配 Spring Boot 2.7.x 最稳妥。Maven 3.6。MySQL 5.7 或 8.0。任意主流 IDE能导入 Maven 项目即可。MySQL 管理工具命令行或者图形化都行。准备好之后先执行 SQL 脚本创建数据库和表再修改application.yml里的数据库密码。这里有个很容易踩的坑Spring Boot 2.7.x 默认用的 MySQL 驱动版本连接 MySQL 8.0 时需要在 JDBC URL 上加参数serverTimezoneAsia/Shanghai和allowPublicKeyRetrievaltrue不加的话大概率会报时区错误或者公钥检索错误。启动项目后浏览器访问http://localhost:8080能看到首页说明基础环境没问题。第一次跑不起来不要急着改代码先看控制台报错信息绝大多数问题都出在数据库连接和端口占用。5.2 打包发布流程本地开发直接mvn spring-boot:run运行验收演示需要打包成可执行 Jarmvn clean package -DskipTests java -jar target/hotel-0.0.1-SNAPSHOT.jar部署到服务器时只要服务器装了 JDK 和 MySQL上传 Jar 包运行同一个命令就能启动。如果服务器 8080 端口被其他程序占用了可以在启动命令加参数java -jar hotel-0.0.1-SNAPSHOT.jar --server.port8081官方文档里的部署章节不要只写“点击运行”要写清楚整个依赖链路数据库需要先初始化、JDK 版本需要匹配、端口不能被占用、前台 IP 访问时数据库账号要有远程访问权限。这些细节对一个要照着做的读者来说非常关键。5.3 配套文档怎么写才好用项目交付时除了源码和数据库文档是加分项。我写的文档分四份README项目简介、技术栈、功能清单、快速启动步骤、默认账号。数据库设计说明每一张表的字段含义、状态字典、表关系描述。用户操作手册从游客浏览到注册、下单、评价的一步步操作截图和文字说明。部署文档环境要求、数据库初始化、打包运行、常见启动报错处理。写文档有个技巧不要复制大段代码要把“为什么这么设计”写清楚。比如订单状态字典表直接列数字是没用必须告诉读者每个状态能执行什么操作、不能执行什么操作。数据库表设计里每张表都加一行“设计目的”比如评价表为什么要关联 order_id 而不是 user_id因为一个用户可能对同一个房型下多次订单评价必须归到具体某次入住经历。6. 常见问题与排查技巧实录6.1 环境与启动类问题启动报错往往是环境问题我把自己遇到过的和帮别人排查过的整理成一张表现象根本原因解决办法启动提示端口被占用8080 被其他程序使用改端口或杀掉占用进程数据库连接失败MySQL 服务没启动或密码错误检查服务状态核对 yml 配置时区相关报错没有配置 serverTimezoneJDBC URL 加serverTimezoneAsia/Shanghai编码乱码数据库建表没用 utf8mb4删除重建使用 utf8mb4白页或 404templates 路径不对或视图名拼错检查 Controller 返回字符串与模板文件名一致端口占用是最常见也最好解决的。Windows 上用netstat -ano | findstr 8080查看 PID再进任务管理器结束进程Linux 上用lsof -i:8080找到进程kill 掉就行。注意被占用的不一定是自己的程序也可能是其他开发工具内置的服务。6.2 业务逻辑类问题业务逻辑出问题最典型的就是日期和状态。日期坑有几个前端传了2025-06-01 00:00:00和后端 DB 里的2025-05-31 23:59:59比较时判断错误或者日期字符串格式化失败页面直接报错。我统一用LocalDate类型接收日期参数Spring 会自动转换比Date类型安全很多。状态是否一致的问题常出现在订单状态更新和房间状态更新不是同时发生的时候。我强调过凡是涉及状态联动的操作必须在同一个事务方法里完成。如果你发现“订单已退房但房间一直显示入住”先看是不是退房的时候只更新了订单状态没更新房间状态。还有一个很隐蔽的问题用户在“我的订单”里取消待支付订单后房间实际上已经释放了这时候页面如果缓存了房间详情可能会误导下一个用户。我在取消订单接口里直接更新了房间状态和订单状态网页端不做缓存每次刷新实时查库。6.3 性能和并发隐患虽然这是个教学性质的项目但写代码时还是要扫一眼性能隐患。第一个隐患是 N1 查询。比如展示订单列表时如果先去查订单再从订单里循环查房间号和用户名那就是典型的 N1。我后来改成一条 SQL 用 JOIN 把订单表、用户表、房型表、房间表关联起来查询一次拿到所有展示字段效率提升非常明显。第二个隐患是房间冲突判断的并发。我前面说了用组合唯一索引兜底这招在真实生产里也不丢人。关键是要在代码里捕获DuplicateKeyException把底层“唯一索引冲突”翻译成用户能看懂的“该房间已被预订”。第三个隐患是订单表的索引。如果订单量上去要保证room_id、check_in_date、check_out_date这三个字段上有组合索引否则日期冲突查询会全表扫描。我建表时加了索引文档里也标了索引字段。一些最后的建议做完这套系统我自己最深的感受是写业务代码前先花一个晚上把表结构画清楚比什么都重要。表关系乱后面写 Service 层会越写越痛苦表设计清楚了接口无非是往里面填逻辑。酒店预订系统这种业务核心就是房态、订单状态、日期区间这三个概念。想明白它们整个项目就已经完成了一半。如果你拿到源码想自己扩展我给几个方向对接真实支付网关比如支付宝或微信支付的模拟接口增加手机验证码登录把静态页面换成 Vue 3 前后端分离用定时任务做超时未支付订单自动取消。每一条都能让项目显得更完整也都有现成资料可查。最后再分享一个小技巧所有枚举含义比如订单状态、房间状态在代码里定义成常量类不要让数字散落在 Service 各处。有人问项目你就直接把常量类打开一边指数字一边讲状态流转比对着文档画流程图更有说服力。
返回列表