温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片!
1. 项目背景与意义
随着城市化进程的不断加快,人们对办公、会议、活动等空间资源的需求日益多样化。传统空间租赁模式存在信息不透明、预约流程繁琐、资源利用率低等问题,大量闲置空间无法得到有效利用。同城共享空间预约平台正是为解决这一痛点而设计,通过互联网技术将城市中的闲置空间资源进行整合与共享,为用户提供便捷的在线预约服务。
本项目的设计与实现具有以下意义:
- 提升资源利用率:将分散的闲置空间集中展示,减少资源浪费,提高空间使用效率。
- 优化用户体验:用户可在线浏览、筛选、预约空间,简化传统线下沟通流程,提升预约效率。
- 促进共享经济发展:推动同城空间资源共享,为空间提供方和需求方搭建高效对接桥梁。
- 提供技术实践参考:基于 SpringBoot 的完整前后端分离实现,为同类共享经济平台开发提供参考。
2. 技术栈选型
本平台采用前后端分离架构,后端基于 SpringBoot 构建,前端使用 Vue.js 配合 Element UI 组件库,数据库选用 MySQL,缓存层使用 Redis。整体技术栈选型如下:
| 层次 | 技术选型 | 说明 |
|---|---|---|
| 后端框架 | SpringBoot 2.7.x | 快速构建独立运行的 Spring 应用,简化配置 |
| 持久层框架 | MyBatis-Plus | 简化 CRUD 操作,内置分页插件,支持代码生成 |
| 数据库 | MySQL 8.0 | 存储用户、空间、订单等核心业务数据 |
| 缓存 | Redis | 缓存热点数据,实现验证码存储与分布式会话 |
| 安全认证 | Spring Security + JWT | 实现用户认证与接口权限控制 |
| 前端框架 | Vue.js 3 + Element UI | 构建响应式单页应用,提供丰富的 UI 组件 |
| 接口文档 | Swagger / Knife4j | 自动生成在线接口文档,便于前后端联调 |
| 构建工具 | Maven | 统一管理项目依赖与构建流程 |
3. 系统功能模块设计
平台面向普通用户、空间提供方和管理员三类角色,核心功能模块划分如下:
3.1 用户端功能
- 用户注册与登录:支持手机号验证码注册登录,使用 JWT 进行身份认证。
- 空间浏览与搜索:按区域、类型、价格、容量等条件筛选空间,支持关键词搜索。
- 空间详情查看:查看空间图片、设施配置、价格规则、可预约时段等详细信息。
- 在线预约:选择预约日期与时间段,提交预约订单,支持在线支付。
- 订单管理:查看预约记录、订单状态,支持取消预约与申请退款。
- 个人中心:管理个人信息、收藏空间、查看历史订单与评价记录。
3.2 提供方端功能
- 空间管理:发布、编辑、上下架自有空间,维护空间基础信息与图片。
- 时段设置:配置空间可预约时段、价格规则与节假日安排。
- 订单处理:查看预约订单,确认或拒绝预约请求,处理退款申请。
- 数据统计:查看空间预约量、收入等经营数据。
3.3 管理员端功能
- 用户管理:审核用户资质,禁用违规账号。
- 空间审核:审核提供方发布的空间信息,确保合规。
- 订单监管:查看全平台订单,处理纠纷与投诉。
- 系统配置:管理公告、分类字典、支付参数等基础配置。
4. 数据库设计
系统核心数据表包括用户表、空间表、订单表、时段表、评价表等。以下为主要表结构设计:
4.1 用户表 t_user
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键,自增 |
| phone | varchar(20) | 手机号,唯一索引 |
| password | varchar(100) | 加密后的密码 |
| nickname | varchar(50) | 昵称 |
| avatar | varchar(255) | 头像地址 |
| role | tinyint | 角色:1-用户,2-提供方,3-管理员 |
| status | tinyint | 状态:0-禁用,1-正常 |
| create_time | datetime | 创建时间 |
4.2 空间表 t_space
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键,自增 |
| provider_id | bigint | 提供方用户 ID |
| name | varchar(100) | 空间名称 |
| type | varchar(20) | 空间类型:会议室、办公室、活动场地等 |
| address | varchar(255) | 详细地址 |
| area | decimal(10,2) | 面积(平方米) |
| capacity | int | 可容纳人数 |
| price | decimal(10,2) | 每小时价格 |
| description | text | 空间描述 |
| status | tinyint | 状态:0-待审核,1-已上架,2-已下架 |
4.3 订单表 t_order
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键,自增 |
| order_no | varchar(32) | 订单编号,唯一 |
| user_id | bigint | 下单用户 ID |
| space_id | bigint | 预约空间 ID |
| start_time | datetime | 预约开始时间 |
| end_time | datetime | 预约结束时间 |
| total_amount | decimal(10,2) | 订单总金额 |
| status | tinyint | 状态:0-待支付,1-已支付,2-已取消,3-已完成,4-退款中 |
| create_time | datetime | 下单时间 |
5. 核心功能实现
5.1 用户认证与 JWT 登录
平台使用 Spring Security 结合 JWT 实现无状态认证。用户登录成功后,后端生成包含用户信息的 JWT Token 返回给前端,前端在后续请求中携带 Token 访问受保护接口。核心实现如下:
@Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Autowired private StringRedisTemplate redisTemplate; @Autowired private JwtUtil jwtUtil; @Override public LoginResponse login(LoginRequest request) { // 1. 校验验证码 String cachedCode = redisTemplate.opsForValue().get("sms:" + request.getPhone()); if (cachedCode == null || !cachedCode.equals(request.getCode())) { throw new BusinessException("验证码错误或已过期"); } // 2. 查询用户,不存在则自动注册 User user = userMapper.selectByPhone(request.getPhone()); if (user == null) { user = new User(); user.setPhone(request.getPhone()); user.setNickname("用户" + request.getPhone().substring(7)); user.setRole(1); user.setStatus(1); userMapper.insert(user); } // 3. 生成 JWT Token String token = jwtUtil.generateToken(user.getId(), user.getRole()); LoginResponse response = new LoginResponse(); response.setToken(token); response.setUserInfo(user); return response; } }5.2 空间预约与冲突校验
预约功能是平台的核心业务。用户提交预约时,后端需要校验所选时段是否与已有订单冲突,避免同一空间同一时段被重复预约。核心实现如下:
@Service public class OrderServiceImpl implements OrderService { @Autowired private OrderMapper orderMapper; @Autowired private SpaceMapper spaceMapper; @Override @Transactional(rollbackFor = Exception.class) public Order createOrder(OrderCreateRequest request, Long userId) { // 1. 校验空间是否存在且已上架 Space space = spaceMapper.selectById(request.getSpaceId()); if (space == null || space.getStatus() != 1) { throw new BusinessException("空间不存在或已下架"); } // 2. 校验时间合法性 if (request.getStartTime().isAfter(request.getEndTime())) { throw new BusinessException("预约开始时间不能晚于结束时间"); } // 3. 校验时段冲突 int conflictCount = orderMapper.countConflict( request.getSpaceId(), request.getStartTime(), request.getEndTime() ); if (conflictCount > 0) { throw new BusinessException("该时段已被预约,请选择其他时间"); } // 4. 计算订单金额 long hours = Duration.between(request.getStartTime(), request.getEndTime()).toHours(); BigDecimal totalAmount = space.getPrice().multiply(BigDecimal.valueOf(hours)); // 5. 创建订单 Order order = new Order(); order.setOrderNo(generateOrderNo()); order.setUserId(userId); order.setSpaceId(space.getId()); order.setStartTime(request.getStartTime()); order.setEndTime(request.getEndTime()); order.setTotalAmount(totalAmount); order.setStatus(0); orderMapper.insert(order); return order; } private String generateOrderNo() { return "SP" + System.currentTimeMillis() + RandomUtil.randomNumbers(4); } }5.3 空间时段冲突查询 SQL
时段冲突校验的核心 SQL 通过判断已有订单的时间区间与新增预约区间是否存在交集来实现,具体如下:
<select id="countConflict" resultType="int"> SELECT COUNT(*) FROM t_order WHERE space_id = #{spaceId} AND status IN (0, 1) AND start_time < #{endTime} AND end_time > #{startTime} </select>5.4 空间列表分页查询
空间列表支持按区域、类型、价格区间等多条件组合筛选,使用 MyBatis-Plus 分页插件实现高效分页查询:
@Service public class SpaceServiceImpl implements SpaceService { @Autowired private SpaceMapper spaceMapper; @Override public PageResult<SpaceVO> querySpacePage(SpaceQueryRequest request) { // 构建分页参数 Page<Space> page = new Page<>(request.getPageNum(), request.getPageSize()); // 构建查询条件 LambdaQueryWrapper<Space> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(Space::getStatus, 1); if (StringUtils.hasText(request.getKeyword())) { wrapper.like(Space::getName, request.getKeyword()); } if (StringUtils.hasText(request.getType())) { wrapper.eq(Space::getType, request.getType()); } if (request.getMinPrice() != null) { wrapper.ge(Space::getPrice, request.getMinPrice()); } if (request.getMaxPrice() != null) { wrapper.le(Space::getPrice, request.getMaxPrice()); } wrapper.orderByDesc(Space::getCreateTime); // 执行分页查询 Page<Space> result = spaceMapper.selectPage(page, wrapper); // 转换为 VO 并返回 PageResult<SpaceVO> pageResult = new PageResult<>(); pageResult.setTotal(result.getTotal()); pageResult.setRecords(result.getRecords().stream() .map(this::convertToVO) .collect(Collectors.toList())); return pageResult; } }6. 系统架构设计
平台整体采用前后端分离的 B/S 架构,前端通过 RESTful API 与后端交互。后端按分层架构组织代码,各层职责清晰、便于维护与扩展:
flowchart TD A[Vue.js 前端] -->|HTTP / RESTful API| B[Nginx 反向代理] B --> C[SpringBoot 后端服务] C --> D[Controller 控制层] D --> E[Service 业务层] E --> F[Mapper 持久层] F --> G[(MySQL 数据库)] E --> H[(Redis 缓存)] C --> I[Spring Security + JWT 认证] C --> J[Swagger 接口文档]后端项目采用标准分层结构,主要包划分如下:
- controller:接收前端请求,参数校验,调用业务层并返回结果。
- service:封装核心业务逻辑,如预约校验、订单状态流转等。
- mapper:基于 MyBatis-Plus 的数据访问层,负责数据库读写。
- entity:数据库表对应的实体类。
- vo / dto:视图对象与数据传输对象,隔离前端展示与后端存储结构。
- config:Spring Security、Redis、Swagger 等配置类。
- common:统一返回结果、异常处理、工具类等公共组件。
7. 总结与展望
本文围绕基于 SpringBoot 的同城共享空间预约平台,从项目背景、技术选型、功能模块、数据库设计、核心代码实现和系统架构等方面进行了系统阐述。平台通过整合同城闲置空间资源,为用户提供便捷的在线预约服务,有效提升了空间资源利用率,具有良好的应用价值。
在后续迭代中,平台可从以下方向持续优化:
- 智能推荐:基于用户历史预约行为,引入推荐算法,为用户推荐更匹配的空间。
- 信用评价体系:完善用户与空间的双向评价机制,建立信用积分体系,提升平台信任度。
- 消息通知:接入