资讯动态

Spring Boot+微信小程序:自习室选座与门禁联动系统设计与实现

发布时间:2026/9/16 16:09:01 来源:尧图企业网站定制
简介一套基于Spring Boot框架与微信小程序端的研学自习室选座及门禁管理系统面向高校图书馆、共享自习室运营者也适合正在学习Java全栈开发、需要完整项目参考的开发者。项目按功能划分为后台管理与前台用户两大板块管理员可维护账户信息、分配用户权限、管理座位占用状态与门禁进出记录并进行数据库连接等系统参数配置用户则能完成注册登录、在线选择座位并预约、通过系统联动门禁进出同时可查看修改个人资料。压缩包共包含749个文件大小约17.09MB主要类型有vue前端页面、wxml/wxss小程序界面、java后端逻辑、js交互脚本、json配置文件等另含sql数据库脚本和bat批处理文件便于快速部署与二次开发。资源目前已有48人浏览学习目录结构清晰、注释与配置齐全既适合作为毕业设计或课程项目的完整体验也可用于生产环境的自习室座位管理场景。1. 为什么自习室选座和门禁要同时放在 Spring Boot 微信小程序里传统自习室管理常用纸质登记加人工开门高峰期座位抢占和门禁混乱是常态。我拆过一套真实交付的研学自习室系统后端是 Spring Boot前端是微信小程序用户在小程序里完成选座后门禁校验逻辑自动联动全程不需要管理员介入。这个项目的价值在于「选座状态」与「门禁记录」两条数据链如何保持一致而不是界面做得有多花哨。它适合刚学完 Spring Boot 和微信小程序、需要一个完整前后端联调案例的开发者也适合要快速交付教室或自习室预约系统的小团队。下面从数据模型、后端接口、小程序交互和部署排错四个层面把每个环节的选型理由和代码细节拆开讲。2. Spring Boot 四层架构与微信小程序端的数据模型设计2.1 从三个批处理文件看这个项目的落地形态拿到压缩包先别急着解压看代码。资源里出现了 main.css.bak、app.9b122259.css、chunk-vendors.a72b0961.css 这一组文件从文件名能判断这是前端构建后的产物说明项目把打包好的页面静态资源直接放进了 Spring Boot 的 static 目录由后端统一托管。配合根目录下的 1-install.bat、2-run.bat、3-build.bat 三个批处理可以看到作者的交付思路先安装依赖再启动后端最后重新构建前端。这种「前后端同仓、打包后合体」的方式适合把源码包交给客户后一键运行。建议先打开三个 bat 文件看一眼内容。1-install.bat 里通常会是mvn install -DskipTests加npm install2-run.bat 是mvn spring-boot:run3-build.bat 则是npm run build然后把 dist 目录里的内容复制到src/main/resources/static。如果你的环境缺少某个命令在这里就能查缺补漏。项目摘要里提到的「数据配置」对应的其实就是application.yml中的数据库连接参数。无论后端还是前端报错先确认这三个文件执行到哪一步比盲目改代码要快得多。2.2 后端四层架构Controller、Service、Mapper、实体Spring Boot 项目最常见的结构是四层架构这套系统也不例外。Controller 层暴露 RESTful APIService 层处理业务规则Mapper 层负责数据库交互实体层映射表结构。以座位管理为例代码里对应的是 SeatController、SeatService、SeatMapper 和 Seat 实体一眼能看出数据流走向。这样的分层对微信小程序端非常友好前端只关心 JSON 字段不关心状态存在哪里。Mapper 层既可以用 MyBatis 直接写 XML也可以用 MyBatis Plus 省略重复 CRUD这个项目从资源文件名看没有额外约束建议你按照自己熟悉的做。但要注意 Service 层不能只是把 CRUD 拼在一起。「选座」这个动作至少包含三步查询座位状态、更新座位状态、写入预约记录。如果这三步不在同一个事务里就会出现在高并发下两个用户选到同一张座位的可能。所以我会在选座方法上直接加Transactional(rollbackFor Exception.class)并且用行锁保护座位记录。这里的关键不是「把三层代码写出来」而是事务边界在哪里、锁粒度选多大这两点决定了系统能不能经得起同时选座的流量。2.3 数据模型用户、座位、预约、门禁四张核心表根据项目简介后台要管理用户、座位、门禁和管理员前台要注册登录、选座和门禁进出。拆表时我认为至少要四张核心表关系如下表名关键字段用途userid, openid, name, role, status前台用户与管理员共用seatid, room_id, seat_no, status, type座位状态空闲/已选/维修reservationid, user_id, seat_id, start_time, end_time, status选座预约记录access_recordid, user_id, seat_id, action, time门禁进出记录特别注意 seat 表的 status 字段我建议在 MySQL 里用tinyint并加注释说明 0 空闲、1 已选、2 维修。Java 实体中只放 Integer 字段不要在每个文件里散落数字更不要用字符串作为状态值因为字符串在索引和比较上都没有数字高效。座位表可以按下面的方式建CREATE TABLE seat ( id BIGINT PRIMARY KEY AUTO_INCREMENT, room_id VARCHAR(32) NOT NULL, seat_no VARCHAR(16) NOT NULL, status TINYINT DEFAULT 0 COMMENT 0空闲 1已选 2维修, type VARCHAR(8) DEFAULT normal, UNIQUE KEY uk_room_seat (room_id, seat_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里room_id和seat_no建立联合唯一索引防止同一房间出现重复座位。seat_no用 VARCHAR 而不是数字因为实际项目中经常出现 A-01、B-10 这种带前缀的编号。type字段为后续扩展留口子比如靠窗座位、电源座位。在 reservation 表上还要加一个联合唯一索引(seat_id, start_time, end_time)让数据库在极端情况下也能拦住重复预约——这是接口层之外的最后一层防线。3. 后端核心实现选座状态机与门禁放行逻辑3.1 座位状态机谁允许谁移动座位状态不能随意跳转。常见的规则是空闲状态只能变已选已选状态可以变空闲用户取消或超时释放也可以变维修管理员发现故障维修状态只能变空闲。小程序端展示的座位颜色就是对 status 字段的映射。前端拿到座位列表后用条件判断不同颜色点击时只有空闲座位可被选中。在 Spring Boot 的 Service 层实现状态流转时不要写一堆散落的 if-else 直接改状态。我通常先定义好合法迁移路径比如空闲-已选、已选-空闲、已选-维修、维修-空闲然后通过一个状态机校验方法判断当前状态是否允许进入目标状态。如果迁移非法就抛出带错误码的异常统一由全局异常处理器返回 JSON。这样前端收到固定的 code 后可以弹对应的 toast比如「该座位已被人选走」或「座位维修中请选择其他座位」。3.2 选座接口事务和行锁是关键假定前端通过POST /api/seat/occupy提交选座请求体传 seatId 和预约时间段请求头带 token。后端核心代码如下PostMapping(/occupy) Transactional(rollbackFor Exception.class) public Result occupy(RequestBody OccupyRequest req, RequestHeader(token) String token) { // 1. 拦截器已经解析出 userId直接使用 Long userId (Long) request.getAttribute(userId); if (reservationMapper.countActiveByUserId(userId) 0) { return Result.fail(4001, 你已有进行中的预约); } // 2. 使用行锁锁定座位避免并发选座 Seat seat seatMapper.selectByIdForUpdate(req.getSeatId()); if (seat null || seat.getStatus() ! 0) { return Result.fail(4002, 座位不可用); } seatMapper.updateStatus(req.getSeatId(), 1); reservationMapper.insert(Reservation.builder() .userId(userId) .seatId(req.getSeatId()) .startTime(req.getStartTime()) .endTime(req.getEndTime()) .status(1) .build()); return Result.success(); }这段代码有三个关键点。第一selectByIdForUpdate在 InnoDB 引擎下是行级锁事务提交前其他请求会被阻塞所以能防止两个用户同时锁定同一张座位。第二countActiveByUserId只统计 status 为 1 的有效预约避免同一个用户同时在两个房间占座。第三请求头里的 token 在拦截器中解析为 userId这部分在本文后面的排错章节会讲。注意事务注解一定加在被 Spring 代理的类上否则不会生效。3.3 门禁放行查有效预约而不是查门禁表门禁和选座不能分开设计。很多初学者会单独建一张门禁权限表用户选座后插记录离开时删除这样两个表很容易不一致。更好的做法是门禁设备或小程序点击开门时后端实时查询用户是否有一条当前有效的预约记录。如果预约有效就放行并写入 access_record如果无效就拒绝并返回错误码。这里可以提供一个GET /api/door/verify接口参数为用户的 openid 或二维码内容。验证 SQL 的重点是时间范围判断start_time NOW() AND end_time NOW()而不是只比较日期否则跨天预约会误判。对于有多个门禁点的自习室access_record 里最好记录 door_id后续可以做各时段客流统计。如果门禁设备走 TCP 或 HTTP 协议Spring Boot 适合把设备调用抽象成一个 DoorAdapter 接口不同品牌设备实现各自的 Adapter业务层只依赖接口方便替换。3.4 管理员接口批量导入和权限控制后台管理相对常规管理员接口可以按下面的表格划分接口路径功能权限POST /api/admin/user创建或禁用用户管理员PUT /api/admin/seat/{id}修改座位状态管理员GET /api/admin/access查询门禁记录管理员POST /api/admin/import-seat批量导入座位管理员需要注意两点。第一管理员和普通用户共用 user 表用 role 字段区分不要建两张表这样登录逻辑和 token 解析可以完全复用。第二批量导入通常是刚需用 EasyExcel 读入 Excel 后逐行校验 room_id 和 seat_no重复数据直接跳过或返回错误行号。如果你的系统需要考虑培训机构多校区的场景还应在 room 表上增加 building_id 字段避免不同校区的房间号冲突。不过这个资源本身没有提到多校区按当前功能设计房间维度已经够用。4. 微信小程序前端选座页面与门禁交互实现4.1 uni-app 还是原生微信小程序从资源里的 chunk-vendors 和 mescroll-uni.css 可以看出前端大概率是基于 uni-app 开发的。uni-app 一套代码可以同时发布微信小程序和 H5适配后台管理端打包进 Spring Boot 静态目录、用户端发布为小程序的场景。如果拿到源码第一件事是打开 pages.json 看路由通常第一个 tabBar 页面是「座位选择」第二个是「预约记录」第三个是「个人中心」。用 HBuilderX 打开工程时记得在「运行设置」里指定微信开发者工具路径否则运行到小程序模拟器会提示找不到工具。开发阶段有一个最容易踩的坑微信小程序要求请求地址是 HTTPS 白名单本地联调时需要在微信开发者工具的「详情 - 本地设置」中勾选「不校验合法域名」。否则你会发现wx.request一直报fail url not in domain list而项目本身没有任何代码问题。4.2 座位图渲染与点击选座逻辑座位图的本质是一个带状态的网格。下面是用wx:for渲染座位格子的示例view classseat-grid view wx:for{{seatList}} wx:keyid classseat-item {{item.status 0 ? free : item.status 1 ? selected : repair}} >public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(token); if (token null || token.isEmpty()) { writeJson(response, Result.fail(1001, 未登录)); return false; } Long userId redisTemplate.opsForValue().get(login:token: token); if (userId null) { writeJson(response, Result.fail(1002, 登录已过期)); return false; } request.setAttribute(userId, userId); return true; } }注册拦截器时用addPathPatterns(/api/**)拦截业务接口用excludePathPatterns(/api/login, /api/door/verify)放行不需要登录的入口。后续所有接口都可以直接从 request 中拿 userId不用重复解析 token。防重复选座可以在 Redis 里用setIfAbsent设置一个 30 秒过期的锁key 为lock:seat:{seatId}请求先获取锁再执行事务。这个方法能覆盖双击按钮造成的重复提交前端 disabled 只是辅助。如果你之后要对接线下客流统计还可以在 access_record 表上按小时聚合用Scheduled定时任务把前一天的数据汇总到 summary 表减少在线查询压力。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价