1. 项目背景与需求分析
图书馆座位资源紧张是高校普遍面临的难题。每到考试周或期末复习季,学生们凌晨排队占座的现象屡见不鲜。传统的人工管理方式存在三大痛点:座位使用率不透明导致资源浪费、占座纠纷频发、管理人员工作负荷大。
基于Android的座位预约系统正是为解决这些问题而生。我开发的这套系统实现了三大核心功能:
- 实时可视化座位状态(空闲/占用/预约中)
- 分时段预约机制(最小30分钟为单位)
- 信用积分管理制度(违约扣除积分)
与市面上现有解决方案相比,本系统创新性地采用了蓝牙信标(iBeacon)室内定位技术。通过在图书馆每个区域部署低功耗蓝牙信标,结合手机信号强度(RSSI)三角定位算法,可以精确判断用户是否实际到馆使用座位,有效防止"只预约不使用"的资源浪费现象。
2. 技术架构设计
2.1 整体架构
系统采用典型的三层架构:
[Android客户端] ←HTTP/JSON→ [SpringBoot服务端] ←JDBC→ [MySQL数据库]客户端使用Android Jetpack组件构建:
- Navigation组件管理页面路由
- Room实现本地数据缓存
- WorkManager处理定时同步任务
服务端关键技术选型:
- Spring Security实现JWT鉴权
- Quartz调度器处理预约超时释放
- WebSocket推送座位状态变更
2.2 核心数据模型
主要数据库表设计如下:
| 表名 | 关键字段 | 说明 |
|---|---|---|
| user | user_id, credit_score | 用户信用积分 |
| seat | seat_id, zone, status | 座位实时状态 |
| reservation | order_id, start_time, duration | 预约记录 |
特别注意status字段采用位掩码设计:
- 第1位:是否可预约
- 第2位:是否有人就座
- 第3位:设备是否在线
这种设计使单个INT字段就能表达多种状态组合。
3. 关键功能实现
3.1 蓝牙定位校验
在SeatCheckService中实现的核心定位逻辑:
// 扫描附近的iBeacon void scanBeacons() { BeaconManager beaconManager = BeaconManager.getInstanceForApplication(this); beaconManager.getBeaconParsers().add(new BeaconParser() .setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24")); RangeNotifier notifier = (beacons, region) -> { if(beacons.size() > 0) { Beacon nearest = Collections.min(beacons, Comparator.comparingDouble(b -> b.getDistance())); if(nearest.getDistance() < 3.0) { // 3米内判定为在位 updateSeatStatus(SeatStatus.IN_USE); } } }; beaconManager.addRangeNotifier(notifier); beaconManager.startRangingBeacons(Region.allBeaconsRegion()); }实测中发现华为/小米等国产手机需要特别处理:
- 需要在Manifest声明ACCESS_FINE_LOCATION权限
- 部分机型需开启GPS才能扫描蓝牙
- EMUI系统有后台扫描限制,需要引导用户设置白名单
3.2 预约状态机
预约流程的状态转换设计:
stateDiagram [*] --> AVAILABLE AVAILABLE --> RESERVED : 用户预约 RESERVED --> OCCUPIED : 扫码入座 OCCUPIED --> AVAILABLE : 使用结束 RESERVED --> AVAILABLE : 超时未签到(15分钟) OCCUPIED --> PENALIZED : 提前离开(<30分钟)这个状态机通过SeatStateMachine类实现,使用State设计模式。关键点在于处理并发修改时的乐观锁控制:
@Transactional public ReservationResult reserveSeat(Long seatId, Long userId) { Seat seat = seatDao.selectForUpdate(seatId); if (seat.getStatus() != AVAILABLE) { return FAILED; } seat.setStatus(RESERVED); seat.setReservedBy(userId); int affected = seatDao.updateWithVersion(seat); return affected > 0 ? SUCCESS : RETRY; }4. 性能优化实践
4.1 座位状态更新风暴
初期采用简单的轮询方案,当500个座位同时更新时:
- 客户端每10秒请求全量数据
- 单次响应数据量达50KB
- 高峰期服务器QPS超过200
优化方案:
- 改用WebSocket长连接
- 服务端只推送变更的座位ID和状态
- 客户端合并更新(debounce 300ms)
优化后数据流量降低87%,核心代码如下:
@GetMapping("/updates") public SseEmitter streamSeatUpdates() { SseEmitter emitter = new SseEmitter(30_000L); seatUpdateListeners.add(emitter); emitter.onCompletion(() -> seatUpdateListeners.remove(emitter)); return emitter; } // 当座位状态变化时 public void onSeatChanged(Seat seat) { seatUpdateListeners.forEach(emitter -> { try { emitter.send( SseEmitter.event() .id(seat.getId().toString()) .data(seat.getStatus()) ); } catch (IOException e) { emitter.complete(); } }); }4.2 离线模式处理
考虑到图书馆部分区域网络信号差:
- 客户端使用Room缓存最近3天的预约数据
- 采用Redux-like的状态管理,所有操作先记录Action
- 网络恢复后按顺序同步到服务端
关键点是处理冲突场景:
- 本地显示"已预约"但服务端实际超时释放
- 采用"最后一次操作获胜"策略
- 通过操作时间戳解决冲突
5. 安全防护措施
5.1 防刷单机制
早期版本出现恶意刷预约问题:
- 同一用户短时间内大量预约/取消
- 使用脚本抢占热门座位
解决方案:
- 滑动窗口限流(令牌桶算法)
- 预约冷却期(取消后5分钟内不能重复预约)
- 行为模式分析(识别异常操作序列)
public boolean allowReserve(Long userId) { String key = "reserve_limit:" + userId; Long count = redisTemplate.opsForValue().increment(key); if (count == 1) { redisTemplate.expire(key, 1, TimeUnit.HOURS); } return count <= 10; // 每小时最多10次 }5.2 扫码安全
座位二维码包含三个要素:
- 座位ID(明文)
- 时间戳(加密)
- HMAC签名(防篡改)
验证逻辑:
public boolean verifyQRCode(String code) { String[] parts = code.split("\\|"); if (parts.length != 3) return false; String hmac = DigestUtils.md5Hex(parts[0] + parts[1] + SECRET_KEY); if (!hmac.equals(parts[2])) return false; long timestamp = Long.parseLong(decrypt(parts[1])); return System.currentTimeMillis() - timestamp < 30_000; // 30秒有效 }6. 部署实施要点
6.1 蓝牙信标部署
实际测试不同位置的信号衰减:
| 安装位置 | 信号覆盖半径 | 穿透能力 |
|---|---|---|
| 桌面下方 | 3-5米 | 弱(金属桌影响大) |
| 立柱侧面 | 5-8米 | 中等 |
| 天花板 | 10-15米 | 强但定位精度低 |
最终采用"每4张桌子1个信标"的部署方案,信标间隔6米,安装高度1.2米(略低于桌面)。
6.2 客户端兼容性
测试覆盖设备清单:
- 华为EMUI(需单独处理后台限制)
- 小米MIUI(默认开启自启动)
- 三星OneUI(蓝牙扫描响应慢)
- 原生Android(表现最稳定)
特别处理:
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/> <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>7. 项目演进方向
当前系统已在某高校图书馆试运行3个月,日均预约量1200+次。后续优化方向:
智能推荐算法
- 根据历史数据预测座位热度
- 结合用户专业推荐相关区域(如工科生偏好安静区)
物联网集成
- 通过座椅压力传感器验证实际使用
- 联动空调/照明实现节能控制
可视化大屏
- 实时展示各区域使用热力图
- 生成资源利用率分析报表
在开发过程中最深刻的体会是:校园场景下的系统设计必须考虑"非理想环境"——网络不稳定、设备型号碎片化、用户操作随意性强。这要求我们在技术方案选择上更注重鲁棒性而非单纯的性能指标。