ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于微信小程序的实验室排课系统设计与冲突检测实现

基于微信小程序的实验室排课系统设计与冲突检测实现 刚把这套系统做完的时候我第一感觉不是兴奋而是如释重负。带过实验课的人都知道排课这事儿看着简单真正做起来就是一场持久战。实验室就那几间课程有几十门老师的上课时间又各不相让以前用Excel表排颜色标得五彩斑斓最后还是会出现两个老师撞同一间实验室的情况。所以当我把基于微信小程序的实验室排课系统这个项目完整跑通——从前端小程序到后端接口从排课算法到文档调试——我觉得这套东西值得拿出来好好聊聊。先说这系统能干什么老师通过微信小程序就能查看自己的实验课表学生能实时看到实验室占用情况管理员在后台完成排课、调课、审批预约全程不需要额外的App安装扫码即用。它解决的痛点是实验室排课中的信息不同步和冲突频发两个老大难问题。对正在做课程设计、毕业设计或者学校里真有这个需求想落地的同学来说这套从源码到文档再到调试的完整方案可以直接当参考模板。下面我把整个项目的设计思路、核心算法、数据库建模、小程序端实现细节还有我在调试过程中踩过的坑一条一条掰开来讲。1. 项目概述与技术选型思路1.1 实验室排课的痛点到底在哪我和好几个实验员聊过大家反馈最强烈的三个问题是第一排课全靠人工记忆Excel哪个老师申请哪个时段全凭一张嘴和一张表改一次课就得重新梳理一遍全局极其容易漏第二实验室的使用状态是黑盒——老师不知道周五下午那间机房有没有人用学生更不知道只能跑到实验室门口看纸质课表第三调课流程走线下纸质审批一个调课申请跑三天黄花菜都凉了。这套系统的核心目标就是把这三点全部线上化。管理员在后台排课系统自动检测冲突老师端小程序随时查看自己的排课和可预约的空闲实验室学生看到的是实时课表哪个时间段哪些实验室空闲一目了然。实现一台手机课表全掌握。1.2 为什么选微信小程序而不是Web或者原生App这个选择我在立项阶段纠结过。学校的教务系统是传统Web端但老师和学生日常根本不会专门打开浏览器去查课表更不会为了看课表安装一个App。微信小程序的本质是用完即走扫码或者从聊天记录里点进去就能用对非技术背景的老师来说学习成本无限接近于零。对比一下几个方案的优劣方案优点缺点传统Web开发快功能全访问路径长移动端体验一般原生App体验好功能强安装成本高维护两套Android/iOS微信小程序免安装入口浅分享方便受平台限制包体有大小上限对于排课这种低频但刚需的场景小程序天然匹配而且学校内部微信群本来就活跃老师点开卡片就能进入系统这比什么推广都好使。1.3 整体系统架构我做的是前后端分离架构小程序端走微信官方原生开发没有上uniapp后面细说原因后端用Spring Boot数据库MySQL。为什么不用uniapp因为我前期测试过uniapp虽然能一套代码多端复用但涉及到复杂的自定义组件和原生控件适配时坑位不少。这个项目里课表组件是我手写的用原生WXML开发调试起来更直接少一层转换就少一层Bug。系统整体分四层展示层微信小程序端负责课表展示、排课操作、预约申请等交互接口层RESTful API统一返回JSON格式数据业务层Spring Boot业务逻辑处理核心是排课冲突检测数据层MySQL 8.0存储用户、实验室、课程、排课记录等核心数据2. 核心功能拆解与数据库设计2.1 角色权限模型一套排课系统里最不能含糊的就是角色权限。我把用户划分成三种角色管理员实验室管理员、教师、学生。这三种人看到的页面和能做的操作完全不同。管理员排课管理、实验室信息维护、预约审批、用户管理教师查看课表、申请调课、预约空闲实验室学生查看课表、查看实验室占用情况、提交预约申请在数据库设计上用户表里直接加一个role字段用1、2、3区分。小程序端根据登录时返回的角色动态渲染不同的tabBar和页面元素。这里有个经验权限控制必须在后端接口层再校验一遍不能只依赖前端隐藏按钮因为接口本身是可以被调用的。我在后端用拦截器统一校验角色权限而不是在每个Controller里写重复的判断代码这也是工程化项目应该有的规范。2.2 功能模块清单用户登录模块微信授权登录后端签发JWT Token实验室管理模块实验室信息的增删改查包括位置、容量、设备配置排课管理模块管理员手工排课系统自动做冲突检测课表展示模块按周视图展示各实验室的排课情况预约管理模块教师/学生提交实验室预约管理员审批消息通知模块排课变更后通过订阅消息通知相关教师2.3 数据库表设计要点数据库设计是排课系统里最需要提前想清楚的部分一旦表结构定错了后面改起来伤筋动骨。我建了五张核心表各表的关键字段和考量如下lab实验室表lab_id主键、lab_name、location、capacity、equipment、statusequipment字段存的是设备清单JSON串比如[投影仪,台式机40台,白板]冗余存储方便小程序端直接展示sys_user用户表user_id、wx_openid、role1管理员/2教师/3学生、name、department、phonewx_openid必须加唯一索引用户表上所有登录查询都依赖它course课程表course_id、course_name、teacher_id、student_count、total_hours课程和教师是单向关联一个教师可以带多门课程schedule排课记录表核心schedule_id、lab_id、course_id、teacher_id、week_day1-7、start_section起始节次、end_section结束节次、week_range周次范围、status排课状态schedule表是整个系统的核心。尤其week_range字段我用了二进制位运算表示1到20周的周值比如第3周是1000二进制数值8第3-16周是11111111111111111100二进制这样只需要一个整数字段就能表示跨多个周的排课区间配合位运算做冲突判断非常快。这个设计是我后来排查一堆排课Bug后回过头优化的堪称本项目的关键点。lab_booking预约表booking_id、user_id、lab_id、book_date、start_section、end_section、purpose、audit_status待审批/已通过/已拒绝3. 排课算法约束、冲突检测与实现3.1 排课场景里的两类约束排课问题本质上是一个资源分配问题难点在于约束条件多且互相牵扯。我把它拆成硬约束和软约束两类。硬约束是必须满足的违反它直接提示冲突同一实验室、同一时间段周次星期节次只能安排一门课同一教师、同一时间段只能安排一门课实验室容量必须大于等于选课人数软约束是尽量满足的比如连排课尽量排在同一实验室避免学生课间换楼、同一门课的实验间隔不要超过两周等。硬约束在排课时必须实时校验软约束通过排课界面里的人工判断和提示来完成。3.2 冲突检测的三种实现方案对比最早我打算在Java内存里遍历所有排课记录做冲突检测数据量小的时候没问题但当排课记录到上千条页面响应时间就上去了。我后来总结出三种方案各有适用场景方案原理适用场景内存遍历从数据库查出全部记录在Java里双重for循环比对数据量小于500条SQL关联查询直接用SQL做时间区间重叠判断数据量中等需要精确判断位运算 索引查询周次用二进制标记配合时间段的BETWEEN条件查询大数据量、高并发场景最终我采用的是方案三schedule表上加了联合索引(lab_id, week_day, start_section, end_section, week_range)。冲突判断的核心代码如下SELECT COUNT(*) FROM schedule WHERE lab_id #{labId} AND week_day #{weekDay} AND start_section #{endSection} AND end_section #{startSection} AND (week_range #{weekRangeMask}) 0这个SQL的精髓在时间区间重叠判断start_section 新结束节次 AND end_section 新起始节次两个排课记录只要时间段有交集这个条件必然成立。加上位运算的周次交集判空一次查询就能把星期几第几节第几周三个维度的冲突全部覆盖。3.3 排课流程与后端核心代码设计排课的业务流程是管理员在小程序端选择实验室、课程、教师、星期、节次、周次范围小程序发起POST /api/schedule/create请求后端进行硬约束校验实验室冲突、教师冲突、容量校验校验通过则写入数据库不通过则返回明确的冲突原因后端服务层方法我写成了模板化的校验流程public Result createSchedule(ScheduleDTO dto) { // 1. 校验教师是否存在且角色正确 // 2. 校验实验室是否存在且状态可用 // 3. 执行冲突检测SQL Integer count scheduleMapper.checkConflict( dto.getLabId(), dto.getWeekDay(), dto.getStartSection(), dto.getEndSection(), dto.getWeekRange()); if (count 0) { return Result.error(该时间段与已有排课冲突请检查); } // 4. 容量校验 if (labService.getCapacity(dto.getLabId()) dto.getStudentCount()) { return Result.error(实验室容量不足); } // 5. 写入排课记录 scheduleMapper.insert(dto); return Result.success(); }这里有个容易被忽略的细节并发场景。两个管理员同时操作排课都通过了冲突检测然后一起写入就可能出现同实验室同时间的双记录。解决方法是在schedule表上建立唯一索引(lab_id, week_day, start_section, week_range)把冲突拦截落到数据库层面就算应用层校验漏了数据库也会报重复键异常。这也是我给这个项目安排的双保险逻辑。4. 小程序端页面与交互实现4.1 项目初始化与目录结构微信小程序的工程结构直接决定了开发效率。我按功能模块划分页面目录而不是按页面类型堆在一起miniprogram/ ├── app.js # 全局逻辑登录态管理 ├── app.json # 全局配置注册页面和tabBar ├── app.wxss # 全局样式 ├── utils/ │ ├── request.js # wx.request封装 │ └── week.js # 周次、节次工具函数 ├── pages/ │ ├── index/ # 首页课表总览 │ ├── schedule/ # 排课管理 │ ├── lab/ # 实验室列表与详情 │ ├── booking/ # 预约申请 │ └── my/ # 我的课表/个人中心 └── components/ ├── timetable-cell/ # 课表单元格组件 └── empty-view/ # 空状态组件app.json里的pages数组第一个元素就是小程序的启动页我把首页课表总览放最前面用户打开就直接看到最近一周的排课情况这比登录页更有价值。4.2 课表可视化页面的实现细节课表是整个项目的门面我花的时间也最多。最初的版本直接用CSS Grid画一个7列×12行的网格每节课一个格子靠grid-row-start和grid-row-end控制跨行。但实测发现不同手机屏幕宽度下格子宽度会溢出因为Grid的列宽没法根据星期标题自适应。后来改成行业里更稳的方案外层用scroll-view横向滚动主表区域用绝对定位让每个排课卡片根据它的星期和节次坐标计算出left和top坐标。伪代码如下scroll-view scroll-xtrue view classtimetable stylewidth: 700px; view wx:for{{scheduleList}} wx:keyid classcourse-card styleleft: {{item.weekDay * 100}}px; top: {{item.startSection * 40}}px; height: {{(item.endSection - item.startSection 1) * 40}}px; {{item.courseName}} /view /view /scroll-view横轴是周一到周日纵轴是按节次均分的刻度。卡片高度由节次跨度算出来这部分逻辑我封装成了week.js工具函数用setData一次性渲染避免循环渲染导致性能恶化。4.3 请求封装与登录态管理小程序端有一个所有项目都必须处理的共性问题wx.request怎么封装才够优雅。我写了一个request.js统一处理基础URL、请求头、状态码、Token过期和网络异常const request (url, method, data) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL url, method: method, data: data, header: { Authorization: Bearer wx.getStorageSync(token) }, success: (res) { if (res.statusCode 401) { // Token过期清理本地登录态重新登录 wx.removeStorageSync(token); wx.navigateTo({ url: /pages/my/my }); reject(res); } else if (res.statusCode 200 res.data.code 200) { resolve(res.data.data); } else { wx.showToast({ title: res.data.message || 请求出错, icon: none }); reject(res); } }, fail: (err) { wx.showToast({ title: 网络异常请重试, icon: none }); reject(err); } }); }); };登录流程我用的是微信官方推荐的wx.login拿到code传给后端换取openid后端生成Token返回。Token存到wx.setStorageSync里每次请求带上。这里我重点强调一个我踩过的坑Token不能只存在Storage里要同时设置一个过期时间戳。有一次我不做过期判断用户一个月后再打开小程序Token早就失效了接口返回401但前端没有自动重新登录课表页面一直白屏。后来我的做法是在app.js的onLaunch里检查Token时间戳如果超时立刻调用wx.login刷新Token让用户无感知续期。5. 调试实录坑与解决方案5.1 开发调试环境的搭建微信开发者工具是整个调试流程的第一站。项目创建后第一件事是到详情-本地设置里勾选不校验合法域名、web-view...、TLS版本以及HTTPS证书。这是官方给开发阶段开的后门不勾的话请求http://localhost:8080直接报url not in domain list。但我必须强调这只是开发环境。真机预览时手机和电脑必须处于同一个局域网后端服务要监听0.0.0.0而不是默认的localhost不然手机根本连不上电脑上跑的Spring Boot服务。5.2 我遇到的5个典型问题问题1真机预览时接口全部超时排查过程开发者工具里一切正常手机上一排request:fail。我先确认手机和电脑在同一WiFi然后ping电脑的局域网IP通了。接着发现Spring Boot的端口绑定了localhost改成0.0.0.0后解决。问题2课表页面数据渲染错乱多个课程卡片叠在一起位置完全乱套。最后定位是setData里数据项没有唯一key小程序渲染时复用组件导致状态串戏。给wx:for加上wx:keyid后解决。问题3角色权限页面跳转异常管理员登录后还显示学生端页面。查了半天是登录接口返回字段是role小程序端代码却读的是userType字段名不一致。自此我定下规矩前后端接口字段命名一定要有一份字段映射文档哪怕项目小也要在注释里写清楚。问题4同一实验室不同周次冲突检测失效排课第1-2周和第3-4周不冲突应该能通过但系统一直提示冲突。这是我最初的week_range用字符串存储导致的两个字符串无法做交集运算。这就是为什么我前面专门强调位运算方案——改了存储方式后冲突判断才真正和需求对齐。问题5小程序包体积超限警告图片资源全部本地化导致主包超过2MB开发工具一直弹警告。处理办法是把所有用于展示的实验室实拍图改为远程URL本地只保留必要的图标。5.3 排课Bug的专项排查技巧排课系统最容易出的Bug就是冲突检测漏判或者误判。这里分享一个我的排查绝招做一份全量排课冲突的验证脚本。在测试阶段我用SQL直接扫描数据库中存在的冲突记录SELECT a.schedule_id AS a_id, b.schedule_id AS b_id, a.lab_id FROM schedule a JOIN schedule b ON a.lab_id b.lab_id AND a.week_day b.week_day AND a.schedule_id b.schedule_id AND a.start_section b.end_section AND a.end_section b.start_section AND (a.week_range b.week_range) 0把这条SQL作为排课质量体检报告每次完成一批排课后跑一遍如果有任何记录返回就说明应用层校验存在漏网之鱼。这种方式在真实排课数据量几百条时非常有效能快速建立对系统可靠性的信心。6. 工程化交付建议与扩展方向6.1 源码与文档的配套规范这个项目配套的文档我做了三份系统设计文档含架构图、数据库ER图、接口文档、部署运维文档环境要求、打包步骤、常见问题、用户操作手册面向管理员和教师的不同流程说明。在源码组织上我给每个模块的包名加了注释说明Controller层统一定义了Result返回对象所有接口的返回格式都是{code, message, data}三件套。这样做的好处很明显前端调用时只需要关心data后端排查问题时只要看code和message就能定位不用到处打日志。6.2 这个系统还能往哪些方向扩展虽然这套系统已经能解决核心需求但排课这个领域其实还有不少可以深挖的地方自动排课加入遗传算法或约束满足算法输入课程和实验室列表系统自动生成最优排课方案。这是从人工排课系统校验跨越到智能排课的关键。消息通知目前用微信订阅消息可以扩展为排课变更后自动推送模板消息减少老师漏看情况。数据大屏在实验室门口放一块大屏轮流展示当天空闲时段和课程安排减少学生白跑。设备预约联动实验室里的特殊设备如高精度仪器独立出来做二次预约让设备使用率更透明。6.3 最后一点个人心得做完这套系统我最大的感触是排课系统的难点不在写代码而在把现实世界的复杂规则准确翻译成代码逻辑。数据库的唯一索引、位运算周次标记、SQL重叠区间判断——每一个技术方案都是在理解了排课到底在排什么之后自然演化出来的。如果你正在做类似的实验室管理系统或者课表项目我建议你先花时间把约束条件列表写全把表和表之间的关系画清楚再动手写代码。架构想清楚了写代码就是水到渠成的事。调试阶段也别怕Bug每一个冲突检测失效、每一个字段名不匹配其实都在帮你校准对业务的理解。这套系统的完整源码、设计文档和调试记录我整理在项目包里了照着部署一遍再对照我这篇经验去看每个模块的实现比单纯啃代码要快得多。
返回列表