面对传统民宿管理模式中存在的“信息割裂、流程混乱、协作效率低下”等问题,本系统聚焦于实现“三端协同运作、业务闭环管理、数据集中可控”的目标。需求划分为功能性与非功能性两大类:
三角色权限体系:
核心业务功能:
为实现模块解耦与高效协作,系统采用经典的三层架构模式(表现层/业务逻辑层/数据访问层),同时设计合理的数据库模型以支撑三端数据流转。
表现层(Web层)
业务逻辑层(Service层)
项目围绕“稳定性、易用性、可扩展性”三大原则,选用成熟稳定的Java Web技术栈,保障系统在多角色并发操作、高频数据交换场景下的可靠运行。具体技术组合如下:
| 技术模块 | 具体工具/技术 | 核心作用 |
|---|---|---|
| 后端框架 | Spring Boot 2.x | 简化项目初始化与配置流程,快速搭建分层结构(Controller/Service/DAO),支持事务管理和依赖注入,显著提升开发效率 |
| 数据库 | MySQL 8.0 | 用于持久化存储用户账户、商家资料、民宿详情、房间库存及订单流水等核心业务数据,支持复杂关联查询,保障数据一致性与完整性 |
| 前端技术 | JSP + Bootstrap + JavaScript | 构建响应式网页界面,适配PC与平板设备,实现表单验证、局部刷新、图表展示等交互体验 |
| 架构模式 | B/S结构 | 无需安装专用客户端,用户仅需通过浏览器即可访问系统,降低使用门槛,支持跨终端同步操作 |
| 开发工具 | Eclipse + Navicat | Eclipse作为主要编码与调试环境,Navicat用于数据库可视化操作(建表、查询、导出备份等) |
| 服务器 | Tomcat 9.0 | 承载Web应用部署,处理HTTP请求,具备良好的并发处理能力,保障平台高可用性 |
| 安全机制 | 密码MD5加密 + 角色权限控制 | 对敏感信息如登录密码进行不可逆加密存储,依据角色划分功能权限,杜绝未授权访问行为 |
随着文化和旅游产业的复苏以及消费者需求的不断升级,民宿作为个性化住宿的重要形式,其市场规模持续扩大。截至2024年,我国民宿市场总规模已突破500亿元,线上预订用户数量超过3亿人。然而,传统的管理方式仍存在诸多瓶颈,如信息分散、操作繁琐、多方协作效率低等问题——许多商家仍依赖Excel表格记录房源信息,客户预订需通过电话或线下沟通完成,管理人员难以统一协调各角色的数据流,严重影响了服务质量和用户体验。
在此背景下,“基于Spring Boot的民宿管理平台”应运而生,致力于打造一个连接“管理员—商家—用户”三方的数字化中枢系统。平台采用B/S架构,借助信息化手段实现民宿信息发布、在线预订、退订审批、投诉处理等全流程线上化管理,建立起“管理员统一监管、商家自主经营、用户便捷消费”的协同机制。该方案为中小型民宿企业提供了一种轻量级、低成本的数字化转型路径,推动行业从“人工驱动”向“智能运营”迈进。
基于Spring Boot框架,系统实现了面向用户、商家和管理端的三端业务逻辑,重点解决“订单状态流转”、“民宿与房间管理”以及“权限控制”三大核心问题。通过分层设计与模块化开发,保障系统的可维护性与扩展性。关键代码如下:
@RestController
@RequestMapping("/api/order")
public class OrderController {
@Autowired
private OrderService orderService;
@Autowired
private RoomService roomService;
/**
* 用户提交房间预订请求
*/
@PostMapping("/book")
public ResponseEntity<?> submitBooking(@RequestBody BookingDTO bookingDTO) {
try {
// 1. 校验指定民宿及房间类型是否存在
Room room = roomService.getRoomByType(bookingDTO.getMinsumingcheng(), bookingDTO.getFangjianleixing());
if (room == null) {
return ResponseEntity.badRequest().body("民宿或房间类型不存在");
}
// 2. (简化处理)默认房间可预订,实际场景中应校验库存或可用日期
// 3. 生成唯一预订编号
String bookingNo = generateBookingNo();
// 4. 封装订单信息对象
BookingOrder order = new BookingOrder();
order.setYudingbianhao(bookingNo);
order.setShangjiabianhao(bookingDTO.getShangjiabianhao());
order.setMinsumingcheng(bookingDTO.getMinsumingcheng());
DAO层负责与数据库进行交互,采用MyBatis作为持久层框架,完成对数据表的增删改查操作,并确保关键事务的一致性。
通过MyBatis将SQL语句封装在Mapper接口中,实现对数据库的CRUD操作。例如:查询用户的预订历史、更新订单当前状态、插入新的退订记录等,均通过XML或注解方式执行SQL。
针对涉及资金与状态变更的核心流程(如订单支付、退订退款),使用Spring的声明式事务管理机制(@Transactional),保证多个数据库操作的原子性,防止出现部分成功、部分失败导致的数据不一致问题。
为支撑完整业务闭环,系统共设计7张核心数据表,覆盖用户、商家、订单、投诉等多维度数据需求,各表结构如下所示:
| 表名 | 核心字段 | 作用说明 |
|---|---|---|
| yonghu(用户表) | id、zhanghao(账号)、mima(密码)、xingming(姓名)、shouji(手机)、zhaopian(头像) | 存储注册用户的基本资料,支持登录认证与个人信息展示 |
| shangjia(商家表) | id、shangjiabianhao(商家编号)、mima(密码)、shangjiamingcheng(商家名称)、lianxidianhua(联系电话)、touxiang(头像) | 保存商家账户信息,用于店铺身份识别与订单归属关联 |
| minsuxinxi(民宿信息表) | id、shangjiabianhao(关联商家)、minsumingcheng(民宿名称)、minsudizhi(地址)、minsutupian(图片)、minsujianjie(简介) | 记录民宿详细信息,供前端页面展示和搜索筛选使用 |
| fangjianxinxi(房间信息表) | id、shangjiabianhao(关联商家)、minsumingcheng(关联民宿)、fangjianleixing(房间类型)、fangjiansheshi(设施)、fangjianjiage(价格) | 描述每个房间的具体配置与定价策略,支撑预订选择逻辑 |
| fangjianyuding(房间预订表) | id、yudingbianhao(预订编号)、shangjiabianhao(关联商家)、minsumingcheng(关联民宿)、zhanghao(关联用户)、tianshu(预订天数)、yishoujine(已收金额)、yudingshijian(预订时间) | 保存所有预订记录,跟踪订单从创建到完成或取消的全过程 |
| fangjiantuiding(房间退订表) | id、tuidingbianhao(退订编号)、yudingbianhao(关联预订)、zhanghao(关联用户)、tuidingshijian(退订时间)、beizhu(退订原因) | 记录用户的退订申请,配合审核流程进行状态更新与退款处理 |
| tousufankui(投诉反馈表) | id、zhanghao(关联用户)、shangjiabianhao(关联商家)、minsumingcheng(关联民宿)、fankuineirong(反馈内容)、shhf(审核回复)、sfsh(审核状态) | 存储用户提交的投诉内容,支持后台处理进度追踪与结果反馈 |
系统通过服务层逻辑严格管控以下核心流程:

// 设置订单信息
order.setFangjianleixing(bookingDTO.getFangjianleixing());
order.setFangjianjiage(room.getFangjianjiage());
order.setTianshu(bookingDTO.getTianshu());
order.setYishoujine(calculateTotal(room.getFangjianjiage(), bookingDTO.getTianshu()));
order.setYudingshijian(new Date());
order.setZhanghao(bookingDTO.getZhanghao());
order.setXingming(bookingDTO.getXingming());
order.setShouji(bookingDTO.getShouji());
order.setIsPay("未支付");
order.setStatus("待确认");
// 保存订单数据
orderService.saveBooking(order);
return ResponseEntity.ok("预订提交成功,等待商家确认,预订编号:" + bookingNo);
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.internalServerError().body("预订失败:" + e.getMessage());
}
/**
* 处理用户发起的退订请求
*/
@PostMapping("/cancel")
public ResponseEntity<?> submitCancel(@RequestBody CancelDTO cancelDTO) {
try {
// 查询对应预订单
BookingOrder order = orderService.getBookingByNo(cancelDTO.getYudingbianhao());
if (order == null) {
return ResponseEntity.badRequest().body("预订订单不存在");
}
// 校验操作权限:仅允许订单所属账户申请退订
if (!order.getZhanghao().equals(cancelDTO.getZhanghao())) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("无退订权限");
}
// 判断当前状态是否支持退订操作
String status = order.getStatus();
if (!"待确认".equals(status) && !"已支付".equals(status)) {
return ResponseEntity.badRequest().body("当前订单状态不支持退订");
}
// 构建退订申请记录
CancelOrder cancelOrder = new CancelOrder();
cancelOrder.setTuidingbianhao(generateCancelNo());
cancelOrder.setYudingbianhao(cancelDTO.getYudingbianhao());
cancelOrder.setShangjiabianhao(order.getShangjiabianhao());
cancelOrder.setMinsumingcheng(order.getMinsumingcheng());
cancelOrder.setFangjianleixing(order.getFangjianleixing());
cancelOrder.setTianshu(order.getTianshu());
cancelOrder.setYishoujine(order.getYishoujine());
cancelOrder.setTuidingshijian(new Date());
cancelOrder.setBeizhu(cancelDTO.getBeizhu());
cancelOrder.setZhanghao(cancelDTO.getZhanghao());
cancelOrder.setStatus("待审核");
// 持久化退订申请,并更新原订单状态
orderService.saveCancel(cancelOrder);
order.setStatus("退订申请中");
orderService.updateBooking(order);
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.internalServerError().body("退订申请提交失败:" + e.getMessage());
}
return ResponseEntity.ok("退订申请已提交,正在等待审核");
}
/**
* 商家发布民宿信息
*/
public MinSu publishMinSu(MinSuPublishDTO publishDTO) {
// 1. 校验商家注册状态
Merchant merchant = merchantMapper.selectByNo(publishDTO.getShangjiabianhao());
if (merchant == null) {
throw new IllegalArgumentException("商家未注册,无法发布民宿信息");
}
// 2. 构建民宿实体对象
MinSu minSu = new MinSu();
minSu.setMingsuBianhao("MS" + System.currentTimeMillis()); // 生成民宿编号
minSu.setShangjiaBianhao(publishDTO.getShangjiabianhao());
minSu.setMingcheng(publishDTO.getMingcheng());
minSu.setDizhi(publishDTO.getDizhi());
minSu.setJingdu(publishDTO.getJingdu());
minSu.setWeidu(publishDTO.getWeidu());
minSu.setRongliang(publishDTO.getRongliang());
minSu.setDanjia(publishDTO.getDanjia());
minSu.setChuangxing(publishDTO.getChuangxing());
minSu.setChuangshu(publishDTO.getChuangshu());
minSu.setZhushiShebei(publishDTO.getZhushiShebei());
minSu.setQitaPeitao(publishDTO.getQitaPeitao());
minSu.setTupianUrls(fileService.uploadFiles(publishDTO.getTupianFiles())); // 图片上传处理
minSu.setStatus("待审核");
minSu.setCreateTime(new Date());
// 3. 保存至数据库
minSuMapper.insert(minSu);
return minSu;
}
/**
* 计算订单总金额(单价 × 天数)
*/
private String calculateTotal(String price, String days) {
BigDecimal unitPrice = new BigDecimal(price);
BigDecimal dayCount = new BigDecimal(days);
return unitPrice.multiply(dayCount).toString();
}
/**
* 生成预订编号:格式为 BK + 当前时间戳 + 随机四位数
*/
private String generateBookingNo() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String dateStr = sdf.format(new Date());
String random = String.valueOf(new Random().nextInt(9000) + 1000);
return "BK" + dateStr + random;
}
/**
* 商家审核退订申请
*/
@PostMapping("/auditCancel")
public ResponseEntity<?> auditCancel(@RequestBody AuditCancelDTO auditDTO) {
try {
// 1. 权限校验:确认当前商家是否有权操作该退订请求
if (!orderService.checkMerchantPermission(auditDTO.getShangjiabianhao())) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("无权限执行审核操作");
}
// 2. 查询退订记录是否存在
CancelOrder cancelOrder = orderService.getCancelByNo(auditDTO.getTuidingbianhao());
if (cancelOrder == null) {
return ResponseEntity.badRequest().body("指定的退订申请记录不存在");
}
// 3. 更新退订状态及审核反馈
cancelOrder.setStatus(auditDTO.getAuditResult()); // 设置审核结果:通过或驳回
cancelOrder.setShhf(auditDTO.getAuditReply()); // 填写商家回复内容
orderService.updateCancel(cancelOrder);
// 4. 同步更新原订单状态
BookingOrder order = orderService.getBookingByNo(cancelOrder.getYudingbianhao());
order.setStatus(auditDTO.getAuditResult().equals("通过") ? "已取消" : "待支付");
orderService.updateBooking(order);
return ResponseEntity.ok("退订审核已完成,最终状态为:" + auditDTO.getAuditResult());
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.internalServerError().body("审核过程中发生错误:" + e.getMessage());
}
}
/**
* 提交退订申请接口
*/
@PostMapping("/applyCancel")
public ResponseEntity<String> applyCancel(@RequestBody CancelApplyDTO applyDTO) {
try {
// 1. 检查订单是否可退
BookingOrder order = orderService.getBookingByNo(applyDTO.getYudingbianhao());
if (order == null) {
return ResponseEntity.badRequest().body("订单不存在");
}
if (!"待入住".equals(order.getStatus())) {
return ResponseEntity.badRequest().body("该订单当前状态不允许退订");
}
// 2. 创建退订申请记录
CancelOrder cancelOrder = new CancelOrder();
cancelOrder.setTuidingbianhao("TK" + System.nanoTime()); // 生成退订编号
cancelOrder.setYudingbianhao(applyDTO.getYudingbianhao());
cancelOrder.setShangjiabianhao(order.getShangjiabianhao());
cancelOrder.setShenqingyuanyin(applyDTO.getReason());
cancelOrder.setStatus("待审核");
cancelOrder.setCreateTime(new Date());
orderService.saveCancelOrder(cancelOrder);
return ResponseEntity.ok("退订申请已成功提交,请等待商家审核");
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.internalServerError().body("提交退订申请失败:" + e.getMessage());
}
}
/**
* 获取商家名下所有民宿
*/
public List<MinSu> getMinSuByMerchant(String shangjiabianhao) {
return minSuMapper.selectByShangJiaBianHao(shangjiabianhao);
}
/**
* 商家发布新民宿
*/
public MinSu publishMinSu(MinSuPublishDTO publishDTO) {
// 1. 校验商家账号是否存在
String shangjiabianhao = publishDTO.getShangjiabianhao();
Merchant merchant = merchantMapper.selectByBianHao(shangjiabianhao);
if (merchant == null) {
throw new RuntimeException("商家账号不存在,请先注册");
}
// 2. 民宿图片上传处理(封面图 + 多张详情图)
String coverUrl = fileService.uploadFile(publishDTO.getCoverFile(), "minsu/cover");
List<String> detailUrls = new ArrayList<>();
for (MultipartFile detailFile : publishDTO.getDetailFiles()) {
if (!detailFile.isEmpty()) {
detailUrls.add(fileService.uploadFile(detailFile, "minsu/detail"));
}
}
// 3. 构建民宿信息实体对象
MinSu minSu = new MinSu();
minSu.setShangjiabianhao(publishDTO.getShangjiabianhao());
minSu.setMinsubianhao(generateMinSuNo()); // 自动生成唯一民宿编号
minSu.setMinsumingcheng(publishDTO.getMinsumingcheng());
minSu.setMinsudizhi(publishDTO.getMinsudizhi());
minSu.setMinsutupian(coverUrl); // 封面图URL
minSu.setMinsujianjie(publishDTO.getMinsujianjie());
minSu.setDetailImages(JSON.toJSONString(detailUrls)); // 详情图片列表转为JSON字符串存储
minSu.setStatus("待审核"); // 初始状态设为待管理员审核
minSu.setAddtime(new Date()); // 记录创建时间
// 4. 插入数据库并返回结果
minSuMapper.insertMinSu(minSu);
return minSu;
}
/**
* 商家修改已有民宿信息
*/
public MinSu updateMinSu(MinSuUpdateDTO updateDTO) {
// 1. 查询目标民宿是否存在,并确认归属权
MinSu minSu = minSuMapper.selectByNo(updateDTO.getMinsubianhao());
if (minSu == null) {
throw new RuntimeException("民宿不存在");
}
if (!minSu.getShangjiabianhao().equals(updateDTO.getShangjiabianhao())) {
throw new RuntimeException("无权限编辑该民宿");
}
// 2. 更新可变字段,仅对非空内容进行覆盖
if (StringUtils.hasText(updateDTO.getMinsumingcheng())) {
minSu.setMinsumingcheng(updateDTO.getMinsumingcheng());
}
if (StringUtils.hasText(updateDTO.getMinsudizhi())) {
minSu.setMinsudizhi(updateDTO.getMinsudizhi());
}
if (updateDTO.getCoverFile() != null && !updateDTO.getCoverFile().isEmpty()) {
String newCoverUrl = fileService.uploadFile(updateDTO.getCoverFile(), "minsu/cover");
minSu.setMinsutupian(newCoverUrl);
}
if (StringUtils.hasText(updateDTO.getMinsujianjie())) {
minSu.setMinsujianjie(updateDTO.getMinsujianjie());
}
// 图片或信息变更后需重新进入审核流程
minSu.setStatus("待审核");
// 3. 执行数据更新操作
minSuMapper.updateMinSu(minSu);
return minSu;
}
@Service
public class AuthService {
@Autowired
private UserMapper userMapper;
@Autowired
private MerchantMapper merchantMapper;
@Autowired
private AdminMapper adminMapper;
/**
* 实现三角色统一登录认证,根据传入的角色类型进行差异化验证
* @param account 登录账号(支持用户账号、商家编号或管理员账户)
* @param password 登录密码(明文输入)
* @param role 用户角色标识(user/merchant/admin)
* @return 返回封装后的登录结果对象,包含状态与基本信息
*/
public LoginResult login(String account, String password, String role) {
// 对原始密码执行MD5加密处理
String encryptedPwd = DigestUtils.md5DigestAsHex(password.getBytes());
switch (role) {
case "user":
User user = userMapper.selectByAccountAndPwd(account, encryptedPwd);
if (user != null) {
return new LoginResult(true, "user", user.getZhanghao(), user.getXingming());
}
break;
case "merchant":
Merchant merchant = merchantMapper.selectByNoAndPwd(account, encryptedPwd);
if (merchant != null) {
return new LoginResult(true, "merchant", merchant.getShangjiabianhao(), merchant.getShangjiamingcheng());
}
break;
case "admin":
Admin admin = adminMapper.selectByAccountAndPwd(account, encryptedPwd);
if (admin != null) {
return new LoginResult(true, "admin", admin.getUsername(), "管理员");
}
break;
default:
return new LoginResult(false, null, null, null);
}
// 所有验证未通过则返回失败结果
return new LoginResult(false, null, null, null);
}
/**
* 权限校验方法:判断指定账号是否具备对应角色的合法身份
* @param account 待校验的账号
* @param role 目标角色类型
* @return 若存在匹配记录则返回true,否则为false
*/
public boolean checkRole(String account, String role) {
switch (role) {
case "user":
return userMapper.selectByAccount(account) != null;
case "merchant":
return merchantMapper.selectByNo(account) != null;
case "admin":
return adminMapper.selectByAccount(account) != null;
default:
return false;
}
}
/**
* 处理新用户或商家注册请求
*/
public void register(RegisterDTO registerDTO) {
if ("user".equals(registerDTO.getRole())) {
// 注册普通用户时,需确保账号在系统中唯一
}
}
// 辅助功能:自动生成民宿编号,规则为“MS”+商家编号后四位+三位随机数
private String generateMinSuNo() {
String merchantSuffix = shangjiabianhao.substring(shangjiabianhao.length() - 4);
String random = String.valueOf(new Random().nextInt(900) + 100);
return "MS" + merchantSuffix + random;
}
// 数据访问层调用示例:根据商家编号查询关联的民宿信息
public MinSu selectMinSuByMerchantNo(String shangjiabianhao) {
return minSuMapper.selectByMerchantNo(shangjiabianhao);
}
}
在注册逻辑处理中,若角色为普通用户,则首先校验账号是否已被使用:
if (userMapper.selectByAccount(registerDTO.getAccount()) != null) {
throw new RuntimeException("用户名已被占用");
}
随后创建用户对象,并将注册信息进行封装。其中密码通过MD5加密存储,头像设置为默认路径,注册时间自动记录:
User user = new User();
user.setZhanghao(registerDTO.getAccount());
user.setMima(DigestUtils.md5DigestAsHex(registerDTO.getPassword().getBytes()));
user.setXingming(registerDTO.getName());
user.setShouji(registerDTO.getPhone());
user.setZhaopian("/static/default-avatar.png");
user.setAddtime(new Date());
userMapper.insertUser(user);
若注册角色为商家,则进入商家注册流程。系统将调用辅助方法生成唯一的商家编号,并初始化商家信息:
} else if ("merchant".equals(registerDTO.getRole())) {
String merchantNo = generateMerchantNo();
Merchant merchant = new Merchant();
merchant.setShangjiabianhao(merchantNo);
merchant.setMima(DigestUtils.md5DigestAsHex(registerDTO.getPassword().getBytes()));
merchant.setShangjiamingcheng(registerDTO.getName());
merchant.setLianxidianhua(registerDTO.getPhone());
merchant.setTouxiang("/static/default-merchant.png");
merchant.setStatus("待审核");
merchant.setAddtime(new Date());
merchantMapper.insertMerchant(merchant);
对于不支持的角色类型,系统抛出异常提示:
} else {
throw new RuntimeException("不支持的注册角色");
}
商家编号的生成规则由独立方法实现,格式为“MER”前缀加当前日期与四位随机数:
private String generateMerchantNo() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
String dateStr = sdf.format(new Date());
String random = String.valueOf(new Random().nextInt(9000) + 1000);
return "MER" + dateStr + random;
}
采用JSP结合Bootstrap技术栈构建系统前端,分别面向管理员、商家和普通用户设计功能清晰、风格统一的操作界面。
管理首页:展示关键数据统计,包括用户总量、商家数量、民宿总数及待处理订单数;提供系统公告编辑功能和快捷入口,如商家审核与投诉处理模块。
商家管理页:列出所有商家信息(编号、名称、电话、状态),支持审核操作(通过或驳回),并可对商家信息进行编辑或删除,允许按状态筛选查看。
民宿审核页:展示待审民宿列表(名称、地址、所属商家、当前状态),支持查看详情(含图片与简介),并执行通过或驳回操作。
订单管理页:集中管理预订与退订订单,显示订单编号、关联用户、民宿及状态,支持审核确认(确认入住或同意退款),并可查看详细信息。
店铺中心:展示商家基本信息(名称、联系方式、头像),提供编辑功能以更新店铺资料,并支持上传新头像。
民宿管理页:列出已发布的民宿(名称、地址、状态),支持新增民宿(填写详情并上传图片),以及对已有民宿进行编辑或删除操作。
房间管理页:展示各房间信息(类型、价格、配备设施),支持新增房间(选择对应民宿并配置价格与设施),也可对现有房间进行修改或移除。
订单处理页:分别列出待确认的预订请求和待审核的退订申请,商家可进行确认或驳回操作,并填写相应回复内容。
前台首页:包含轮播图展示热门推荐民宿,提供搜索框支持按名称或地址筛选,下方以卡片形式呈现民宿列表,包含图片、名称与价格等信息。
民宿详情页:支持图片轮播展示,显示民宿具体位置、详细介绍,并列出可选房间类型(含价格与设施说明),页面提供预订与收藏功能按钮。
个人中心:用户可查看和管理个人信息(姓名、手机号、头像),浏览我的订单(含预订与退订记录及状态)、管理收藏的民宿(支持取消收藏),以及提交投诉反馈并查看处理回复。
为验证系统的功能完整性与运行稳定性,实施了多维度的系统测试,重点覆盖管理端、商家端和用户端的核心业务流程,并包含各类异常场景的处理能力。
针对管理员、商家及用户的典型操作设计测试用例,确保各功能模块按预期执行。主要测试内容如下:
| 测试场景 | 预期结果 | 实际结果 | 是否通过 |
|---|---|---|---|
| 商家注册与审核 | 提交后状态为“待审核”,管理员审核通过后可登录 | 流程正常,状态更新准确 | 是 |
| 民宿发布与展示 | 发布需审核,审核后在首页展示 | 信息完整显示,审核机制有效 | 是 |
| 房间预订流程 | 用户预订→商家确认→支付完成→订单状态变更为“已支付” | 状态流转正确,数据同步及时 | 是 |
| 退订申请处理 | 用户发起退订→商家审批→订单状态更新为“已取消” | 逻辑清晰,状态变更无误 | 是 |
| 投诉反馈处理 | 用户提交投诉→商家回复→用户可查看回复内容 | 消息传递正常,数据实时同步 | 是 |
从兼容性、性能、安全性和数据一致性四个方面进行深入验证,确保系统在真实环境中的可靠表现。
兼容性测试:在Chrome 120、Firefox 119、Edge 120等主流浏览器中对三端界面进行测试,页面布局未出现错乱,核心功能均可正常使用。
性能测试:模拟50个用户同时浏览民宿列表时,页面平均加载时间控制在1.8秒以内;10名用户并发提交预订请求时,系统平均响应时间不超过1.2秒,满足基本性能要求。
安全测试:普通用户尝试访问管理员后台路径(如/admin)时被成功拦截并跳转至登录页;在登录框输入SQL注入语句(如‘ or 1=1 #’),系统能有效过滤并提示“非法输入”,具备基础防护能力。
数据一致性测试:当商家删除某民宿时,其关联的房间信息及未完成订单均被标记为“无效”;用户注销账号后,相关预订记录保留但敏感信息脱敏处理,收藏数据则被清除,符合隐私与业务规范。
在开发过程中识别出若干典型问题,并采取针对性方案进行优化,显著改善系统稳定性与交互体验。
问题描述:图片虽成功上传至服务器本地目录,但通过URL访问时报404。
解决方案:在Tomcat配置文件中设置虚拟路径映射(例如/upload/minsu/cover/指向实际存储路径),前端请求时拼接完整虚拟地址(如http://localhost:8080/upload/minsu/cover/xxx.jpg),实现资源正常访问。
问题描述:多个用户同时预订同一间房(库存仅1间),可能出现重复下单成功的情况。
解决方案:在房间表中引入“库存”字段,预订操作采用数据库事务结合行级锁(SELECT ... FOR UPDATE)机制,确保库存检查与扣减过程原子化,避免并发冲突。
问题描述:管理员完成民宿审核后,商家必须手动刷新页面才能看到状态变化,影响使用体验。
解决方案:集成WebSocket实现实时通信功能,审核完成后系统主动向前端推送通知(如弹窗提示“您的民宿已通过审核”),无需刷新即可获取最新状态。
问题描述:当民宿数量较多时,首页一次性加载全部数据导致页面卡顿。
解决方案:引入分页机制(默认每页展示10条),图片启用懒加载技术(滚动至可视区域再加载),并对热门民宿列表使用Redis缓存(有效期1小时),大幅减少数据库查询压力,提升响应速度。
本项目提供完整的开发与部署资料,便于学习者理解系统结构并开展二次开发:

扫码加好友,拉您进群



收藏
