省钱兄 JAVA 无人共享篮球场馆预约系统源码解析
支持 小程序 + 公众号 + APP + H5 全端覆盖
系统整体架构
层级技术选型后端框架SpringBoot 2.7.x + MyBatisPlus 3.5.0数据库MySQL 8.0 + Redis 6.2(缓存/分布式锁)用户端UniApp(Vue3)→ 编译为 小程序/公众号H5/APP/H5管理后台Vue3 + ElementPlus + ECharts部署JDK 17 + Tomcat 9 + Nginx + Docker支付微信支付 + 支付宝(异步回调)消息推送WebSocket +
核心功能模块1️⃣ 篮球场馆管理java@Data @TableName("basketball_court")public class BasketballCourt { @TableId(type = IdType.AUTO) private Long id; private String name; https:// 场馆名称 private String location; https:// 地址 private Integer status; https:// 0-空闲 1-已预约 2-维修中 private BigDecimal pricePerHour; https:// 每小时价格 private String imageUrl; https:// 场馆图片 private String ; https:// 场地二维码}2️⃣ 预约核心流程(防超卖)java@Service @Transactionalpublic class ReserveOrderService { @Autowired private RedisTemte<String,Object> redisTemte; /** * 预约接口 - 分布式锁防止超卖 */ public Result reserve(ReserveDTO dto) { String lockKey = "court_lock:" + dto.getCourtId(); try { Boolean locked = redisTemte.opsForValue() .setIfAbsent(lockKey, "1", 30, TimeUnit.SECONDS); if (!locked) { return Result.fail("当前场地正在被其他用户预约"); } https:// 1. 检查场地是否可用 BasketballCourt court = courtMapper.selectById(dto.getCourtId()); if (court.getStatus() != 0) { return Result.fail("该场地当前不可用"); } https:// 2. 创建预约订单 ReserveOrder order = createOrder(dto); https:// 3. Redis 扣减库存(库存=时段数) String stockKey = "court_stock:" + dto.getCourtId(); Long stock = redisTemte.opsForValue().decrement(stockKey); if (stock < 0) { redisTemte.opsForValue().increment(stockKey); return Result.fail("该时段已被预约满"); } https:// 4. 更新场地状态 court.setStatus(1); courtMapper.updateById(court); return Result.success(order); } finally { redisTemte.delete(lockKey); } }}3️⃣ 扫码入场(无人化核心)java@Servicepublic class ScanAccessService { /** * 扫码入场 - 核心无人化流程 */ public AccessResult scanToEnter(uest request) { https:// 1. 验证预约订单 ReserveOrder order = orderMapper.selectByOrderNo(re()); if (order == null || order.getStatus() != OrderStatus.PAID) { return AccessResult.fail("无效的预约订单"); } https:// 2. 验证时间窗口(前后30分钟内有效) LocalDateTime now = LocalDateTime.now(); if (now.isBefore(order.getStartTime().minusMinutes(30)) || now.isAfter(order.getEndTime().plusMinutes(30))) { return AccessResult.fail("不在有效入场时间内"); } https:// 3. 生成入场二维码(一次性) String accessCode = UUID.randomUUID().toString(); redisTemte.opsForValue().set("access:" + accessCode, order.getCourtId(), 30, TimeUnit.MINUTES); return AccessResult.success(accessCode); } /** * 扫码出场 - 自动结束计费 */ public SettlementResult scanToExit(String accessCode) { String courtId = (String) redisTemte.opsForValue().get("access:" + accessCode); if (courtId == null) { return SettlementResult.fail("无效的入场码"); } https:// 计算时长 → 结算金额 → 退还押金 ReserveOrder order = orderMapper.selectByCourtId(Long.parseLong(courtId)); BigDecimal duration = Duration.between(order.getStartTime(), LocalDateTime.now()).toMinutes(); BigDecimal fee = duration.divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP) .multiply(order.getPricePerHour()); https:// 退还押金 - 费用 walletService.refund(order.getUserId(), order.getDeposit().subtract(fee)); return SettlementResult.success(fee); }}4️⃣ 支付回调处理java@PostMapping("/pay/notify")public String payNotify(uest request) { https:// 验证微信签名 boolean verified = verifySign(request); if (!verified) return "fail"; String orderNo = rer("out_trade_no"); https:// 更新订单状态 orderService.updateOrderStatus(orderNo, OrderStatus.PAID); return "success";} 前端核心页面(UniApp)
页面路径功能首页pages/index/index场馆列表、附近场馆、搜索场馆详情pages/detail/detail场地图片、价格、时段选择预约页面pages/reserve/reserve日历选择 + 时间段 + 支付扫码入场pages/scan/scan扫码 → 生成入场码个人中心pages/user/user订单记录、钱包、会员
预约日历组件(核心)javascriptexport default { data() { return { currentDate: new Date(), selectedSlots: [], bookedSlots: [] https:// 已被预约的时段 } }, methods: { checkSlotAvailable(slot) { return !this.bookedSlots.includes(slot.id) }, handleSelectSlot(slot) { if (!this.checkSlotAvailable(slot)) return const index = this.selectedSlots.findIndex(s => s.id === slot.id) if (index > -1) { this.selectedSlots.splice(index, 1) } else { this.selectedSlots.push(slot) } }, async submitReservation() { uest({ url: 'https://api.example.com/reserve', method: 'POST', data: { courtId: this.courtId, date: this.form.date, timeSlot: this.form.timeSlot }, success: (res) => { if (res.data.code === 200) { uni.navigateTo({ url: '/pages/payment?orderId=' + res.data.data }) } } }) } }}️ 核心数据库表
表名说明users用户表(openid/手机号/余额)basketball_court篮球场馆表time_slots时段表(每30分钟一个时段)reserve_order预约订单表pay_record支付记录表access_log扫码进出记录表wallet_log钱包流水表
s TABLE reserve_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(64) UNIQUE NOT NULL, user_id BIGINT NOT NULL, court_id BIGINT NOT NULL, start_time DATETIME NOT NULL, end_time DATETIME NOT NULL, total_amount DECIMAL(10,2), deposit DECIMAL(10,2), -- 押金 order_status TINYINT DEFAULT 0, -- 0待支付 1已支付 2使用中 3已完成 4已取消 create_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_court_time (court_id, start_time, end_time)); 部署方案dockerfile# DockerfileFROM openjdk:17-jdk-slimCOPY target/basketball-app.jar /app.jarEXPOSE 8080ENTRYPOINT ["java", "-jar", "/app.jar"]
组件方案自动化部署Jenkins + Docker监控Prometheus + Grafana日志ELK Stack缓存Redis Cluster消息队列(高峰期削峰)
系统亮点总结
特性说明✅ 全端覆盖小程序/公众号/APP/H5 一套代码✅ 无人化运营扫码入场 → 自动计费 → 扫码出场 → 自动结算✅ 防超卖Redis分布式锁 + 乐观锁✅ 高并发削峰 + Redis缓存✅ 智能推荐协同过滤算法推荐附近热门场馆✅ 数据看板ECharts实时展示营收/热力图/高峰时段
获取完整源码:此类系统通常在 CSDN、GitCode 等平台以"省钱兄"或"无人共享篮球预约"为关键词发布,搜索 "JAVA无人共享篮球预约系统源码" 即可找到对应开源项目,一般包含完整的前后端源码 + 数据库脚本 + 部署文档。
省钱兄科技,无人共享系统,无人场馆,预约篮球