简介:一套面向高校毕业设计/课程设计的睡眠检测微信小程序完整工程,适合计算机相关专业学生参考与二次开发。功能上涵盖助眠音乐、睡眠数据记录(支持日期筛选和睡眠次数/时长统计,借助ECharts形成线性趋势图)、健康知识问答(图文/视频展示)、个人信息维护等模块,几乎覆盖典型的“睡眠助手”类毕设需求。包内共110个文件,核心包括前端JS/JSON/WXML/WXSS源码、MySQL数据库脚本与后端接口逻辑,另有53张页面截图、2个mp4操作演示视频、1个gif动图,以及说明文档、开题报告、设计文档等过程性材料,整体仅14.63MB,开发环境可适配JDK1.8、MySQL5.7及以上、HBuilder X/微信开发者工具等常见组合。目前该资源已有170人学习/浏览,可作为睡眠监测课题的完整参考方案,帮助快速搭建环境、理解前后端交互,并依托文档完成毕设说明与答辩准备。
1. 睡眠检测小程序源码是什么:一份能跑通全流程的毕业设计交付物
拿到这样一份 zip 时,大多数人第一反应是解压后找"能不能直接跑"。标题里的信息其实已经把交付范围讲清楚了:一个微信小程序前端 + 一套后端接口 + 一个 MySQL 数据库,再加上说明文档和 LW(论文),合起来是一份"从数据库到手机屏幕全打通"的毕设工程。睡眠检测这个方向听起来玄学,实际落地逻辑并不复杂——小程序端通过加速度传感器判断用户是否处于翻身频繁的状态,结合用户点击"开始睡眠 / 结束睡眠"的时间点,把一段睡眠数据上报给后端,落进 MySQL,再在历史页面里用图表展示。适合两类人:一类是拿它做毕业设计、需要快速改成自己项目的学生,另一类是接小程序定制单、想复用一套基础代码的开发者。
2. 先把源码包拆明白:前后端分工与 MySQL 表设计
拿到源码包之后不要急着跑,先花半小时把目录结构和数据流理清楚。这套方案能成为"完整前后端",核心就在于它把数据采集、业务处理、存储三层拆开了。下面按我拿到这类包的习惯顺序,从前端到后端到数据库逐个说。
2.1 从目录结构看这个项目为什么是"前后端分离"
小程序天生就是前后端分离的形态:微信开发者工具里跑的是前端工程,里面是pages、utils、components这类目录;后端是独立的服务工程,用 Spring Boot、SSM 或者 Node/Express 写的都有,标题没限定语言,但大部分毕设包会用 Spring Boot + MyBatis,因为答辩时好讲三层架构。
我一般拿到包会先看三样东西。第一是app.json,它决定小程序有哪些页面、窗口标题、tabBar 配置,能一眼看出功能模块划分。第二是后端的pom.xml(Maven 工程)或package.json(Node 工程),确认依赖版本和框架选型。第三是sql目录或db目录下的初始化脚本,里面有建库建表语句,是整个项目的数据地基。
看目录时最容易忽略的是配置文件。小程序的utils/request.js里通常写死了一个 baseUrl,比如http://localhost:8080或某个局域网 IP;后端的application.yml里配置了 MySQL 地址、端口、账号密码。这两个地方是"前后端联调"的第一道关卡,后面排查联调问题时有九成概率要回来改这里。
2.2 睡眠记录表设计:sleep_record 的字段、类型与默认值
睡眠检测的数据模型是整个项目的核心,因为它直接决定了后端接口怎么写、前端图表能画出什么。绝大多数这类项目的核心表是两张:用户表和睡眠记录表。用户表负责登录鉴权,字段简单;睡眠记录表才是核心,我建议至少包含这些字段。
| 字段 | 类型 | 说明 |
|---|---|---|
| id | BIGINT | 主键,自增 |
| user_id | BIGINT | 关联用户表 id |
| sleep_date | DATE | 睡眠日期,按本地日期存,避免跨天查询混乱 |
| start_time | DATETIME | 入睡时间 |
| end_time | DATETIME | 醒来时间 |
| duration_minutes | INT | 睡眠总时长,单位为分钟,默认值 0 |
| deep_minutes | INT | 深度睡眠分钟数,传感器判定不了就默认 0 |
| awake_count | INT | 翻身/清醒次数,用于描述睡眠质量 |
| status | TINYINT | 0=正在进行中,1=已完成,2=异常中断 |
| remark | VARCHAR(255) | 备注,默认空字符串 |
字段里需要特别留意的有三个。第一个是status,它必须默认 0,因为用户点"开始睡眠"时记录就已经创建了,只是还没有结束时间;结束上报时才把它更新为 1。第二个是duration_minutes,默认值设为 0,不能为 NULL,否则前端图表取值时要处理空值,容易翻车。第三个是时间字段,建议start_time、end_time用 DATETIME 而不是 TIMESTAMP,DATETIME 不依赖数据库时区,后面讲时区坑的时候会细说。
2.3 运行环境选型:JDK、Node、MySQL 版本怎么搭配
环境版本不匹配是这类源码包"跑不起来"的第一大原因。常见做法是后端用 JDK 1.8 或 11(Spring Boot 2.x 对这两个版本支持最稳),小程序端用微信开发者工具最新稳定版,数据库用 MySQL 5.7 或 8.0。如果源码包里的pom.xml用的是 Spring Boot 3.x,那 JDK 必须 17 以上,这是最容易忽略的硬性条件。
MySQL 安装这里单独提醒一点:很多人在 Windows 上按"mysql 安装教程 8.0"的步骤装完后,发现后端报Unable to load authentication plugin,这是 8.0 默认认证插件从mysql_native_password换成了caching_sha2_password,老版本的 JDBC 驱动不认识。解决方案有两个方向:要么把连接驱动升级到 8.x,要么建用户时指定老插件。也有人图方便用 Docker 装 MySQL,但docker run时忘记映射端口或没设置MYSQL_ROOT_PASSWORD环境变量,会导致后端连不上——这就是"docker 安装 mysql 失败"最常见的原因,不是镜像问题,是参数问题。
3. 小程序端睡眠检测怎么做:传感器监听、页面与数据上报
小程序端是整个项目的"脸面",也是答辩时演示的重点。睡眠检测的实时判断逻辑、页面交互、数据上报都在这层。原生微信小程序和 uniapp 打包的工程结构不同,但核心 API 一致,下面以原生小程序为例讲。
3.1 睡眠检测原理:靠加速度计判断"睡没睡"
睡眠检测不是靠摄像头或心率,绝大多数毕设方案用wx.onAccelerometerChange监听加速度变化。手机放在枕头边时,人翻身会带动加速度明显变化,静止时加速度数值平稳。程序的做法是:用户点"开始睡眠"后,小程序以固定频率采集加速度数据,计算每个采样点的加速度模值,和上一次采样做差值,超过阈值就记一次"翻身"。
这个方案听起来简单,实际调起来很"玄学"——阈值设大了,翻身都检测不到;设小了,正常呼吸放手机的动作都会触发误判。我一般建议按三步调:先把采样间隔固定,再调阈值,最后调结算窗口。参考逻辑如下。
const THRESHOLD = 1.6; // 加速度差值阈值,单位 m/s² const SETTLE_WINDOW = 30000; // 结算窗口,30 秒统计一次 let moveCount = 0; let isSleeping = false; let timer = null; function startMonitor() { // interval 可选 game / ui / normal,game 频率最高,约 60ms 一次 wx.startAccelerometer({ interval: 'game' }); wx.onAccelerometerChange((res) => { const magnitude = Math.abs(res.x) + Math.abs(res.y) + Math.abs(res.z); if (magnitude > THRESHOLD) { moveCount += 1; } }); timer = setInterval(() => { // 窗口期内翻身次数少于阈值,判定为睡眠中 isSleeping = moveCount < 5; console.log('睡眠状态:', isSleeping, '翻身次数:', moveCount); moveCount = 0; }, SETTLE_WINDOW); }逻辑说明:加速度计每次回调拿到 x/y/z 三个轴的值,取绝对值之和作为"运动强度"的近似。超过阈值的采样点计一次翻身,每 30 秒结算窗口看累计次数是否小于 5,小于就认为处于睡眠状态。这里的核心是"移动计数 + 窗口决策"两步,而不是单点判断,能过滤掉短暂抖动。
参数说明:THRESHOLD建议从 1.2 到 2.0 之间试,手机放床上和拿在手里测出来的数据完全不同;SETTLE_WINDOW越长越稳但实时性差,30 秒是折中值;moveCount < 5表示 30 秒内翻身小于 5 次,如果你测试时发现躺着玩手机也被判为睡眠,就把这个上限从 5 降到 3。
3.2 核心页面与代码:监听、上报、防重复提交
页面结构一般分三块:首页是"开始睡眠 / 结束睡眠"的按钮和实时状态展示,历史页是睡眠记录的列表和图表,我的页面是用户信息和登录入口。首页的结束按钮是整个流程的关键,用户点结束时要把开始时间、结束时间、翻身次数一起上报。上报接口必须考虑重复提交,很多毕设翻车就翻在"连点两次结束,生成两条记录"。
function submitSleep(record) { const token = wx.getStorageSync('token'); if (!token) { wx.showToast({ title: '请先登录', icon: 'none' }); return; } wx.request({ url: 'http://localhost:8080/api/sleep/end', method: 'POST', header: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token }, data: record, success: (res) => { if (res.statusCode === 200) { wx.showToast({ title: '记录已保存' }); // 保存成功后禁用按钮,防止再次点击 this.setData({ submitting: false }); } else { wx.showToast({ title: '上报失败: ' + res.statusCode, icon: 'none' }); } }, fail: (err) => { // 常见原因:后端没启动、局域网 IP 不通、域名未加入白名单 console.error('request fail', err); wx.showToast({ title: '网络异常', icon: 'none' }); } }); }逻辑说明:上报前先取本地缓存的 token,没有就提示登录。请求头里带Authorization,后端靠它识别用户身份。成功后把按钮的submitting置为 false 禁用按钮,这也是前后端对于按钮重复提交校验方法里最前端的一层——后端还要做幂等检查,双保险。
参数说明:url在开发阶段填局域网 IP 加端口,比如http://192.168.1.10:8080,真机调试时手机和电脑必须连同一个 WiFi;Authorization的 Bearer 前缀是约定俗成的写法,后端解析时会去掉这个前缀取 token 本身。
3.3 自定义导航栏与真机适配:顶部导航栏高度和权限声明
历史页如果要用 canvas 画图表,通常会遇到标题栏遮挡问题。很多毕设从网上复制了"自定义导航栏"的代码,却漏了适配顶部导航栏高度——iPhone 的刘海屏和普通安卓机的状态栏高度不一样。常见做法是在app.json里把navigationStyle设为custom,然后在页面里用wx.getWindowInfo()拿状态栏高度,动态计算内容区的padding-top。
另外,真机调试时加速度计拿不到数据,九成是缺权限声明。需要在app.json里加一段permission配置:
{ "permission": { "scope.accelerometer": { "desc": "用于检测睡眠状态变化" }, "scope.userInfo": { "desc": "用于登录和个性化展示" } } }这段配置的作用是:小程序在用户手机上申请传感器权限时,弹窗里会显示desc里的说明文案。不写这配置,开发工具里模拟器能跑,但一上真机,授权弹窗可能直接失败或默认拒绝。还有一个隐蔽问题:iOS 微信对后台传感器监听有限制,息屏一段时间后加速度计回调会停止,所以"结束睡眠"按钮必须在 App 处于前台时操作,睡眠中息屏检测是理想情况,实际只能靠用户清醒后手动结束。
4. 后端接口与 MySQL 落库:三层结构、建表 SQL 与联调约定
前端把数据采上来,后端负责接住并落库。这一章讲后端最常见的工程结构、建表脚本,以及三个核心接口的请求响应约定。后端是答辩时被问得最多的部分,架构能讲清楚,基本就成功了一半。
4.1 后端组织方式:controller / service / mapper 与事务处理
这类毕设的后端通常分三层:Controller 接收前端请求,Service 写业务逻辑,Mapper 操作数据库。Controller 层只做参数校验和结果封装,Service 层处理业务规则,Mapper 层对应 SQL。三层的好处是答辩时能顺着"请求从哪里进来、数据怎么流转"讲一节课。
事务处理是必须提的一个点。结束睡眠上报涉及两步:先查今天是否已有记录,再插入或更新记录。这两步必须放在同一个事务里,否则并发请求打过来时,两个请求同时查到"没有记录",然后各自插入一条,就产生了重复数据。解决办法有两个层面:代码层面用 Spring 的@Transactional保证原子性,数据库层面用唯一索引兜底。
@RestController @RequestMapping("/api/sleep") public class SleepRecordController { @Autowired private SleepRecordService sleepRecordService; @PostMapping("/end") @Transactional(rollbackFor = Exception.class) public Result<Long> endSleep(@RequestBody SleepEndRequest req, @RequestHeader("Authorization") String auth) { Long userId = TokenUtil.parseUserId(auth); if (userId == null) { return Result.error(401, "登录状态已过期"); } // 幂等检查:同一天同一用户已存在已完成记录,直接返回 if (sleepRecordService.hasFinishedRecord(userId, req.getSleepDate())) { return Result.error(400, "今天已上报过,请勿重复提交"); } SleepRecord record = sleepRecordService.buildAndSave(userId, req); return Result.success(record.getId()); } }逻辑说明:接口先解析 token 拿 userId,然后做幂等检查——同一用户同一天如果有已完成记录,直接拒绝本次提交。这个检查对应前端按钮禁用的第二道防线,也是防重复提交校验方法里后端最正规的写法。
参数说明:@Transactional指定异常时回滚,避免插入了一半数据;TokenUtil.parseUserId是工具类,从Authorization头里解析出用户 ID,实际项目里会用 JWT 或自定义 token 机制。
4.2 建表 SQL:初始化数据库的完整脚本
数据库脚本是整个工程的起点。拿到源码包后,先执行这个 SQL 再启动后端,是最不容易出错的操作顺序。下面是一份按前面表设计写的完整建表脚本。
CREATE DATABASE IF NOT EXISTS sleep_health DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE sleep_health; CREATE TABLE t_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID', username VARCHAR(32) NOT NULL UNIQUE COMMENT '登录账号', password VARCHAR(64) NOT NULL COMMENT '密码,建议存 bcrypt 哈希', nickname VARCHAR(32) DEFAULT '用户' COMMENT '昵称', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' ) ENGINE=InnoDB COMMENT '用户表'; CREATE TABLE t_sleep_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键', user_id BIGINT NOT NULL COMMENT '关联 t_user.id', sleep_date DATE NOT NULL COMMENT '睡眠日期', start_time DATETIME NOT NULL COMMENT '入睡时间', end_time DATETIME NOT NULL COMMENT '醒来时间', duration_minutes INT DEFAULT 0 COMMENT '睡眠总时长(分钟)', deep_minutes INT DEFAULT 0 COMMENT '深度睡眠分钟数', awake_count INT DEFAULT 0 COMMENT '翻身次数', status TINYINT DEFAULT 0 COMMENT '0=进行中 1=已完成 2=异常中断', remark VARCHAR(255) DEFAULT '' COMMENT '备注', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_user_date (user_id, sleep_date), CONSTRAINT fk_sleep_user FOREIGN KEY (user_id) REFERENCES t_user(id) ) ENGINE=InnoDB COMMENT '睡眠记录表';逻辑说明:库和表的字符集都用 utf8mb4,避免 emoji 昵称写入报错;t_sleep_record和t_user通过外键关联,保证数据完整性。索引放在(user_id, sleep_date)上,因为历史页最常见的查询就是"查某个用户某段时间的记录",这个联合索引能让查询走索引而不是全表扫。
参数说明:duration_minutes默认值为 0,对应前面说的"mysql 设置默认值为 0"的实践,前端拿到的永远不是 NULL;status默认 0 表示记录刚开始,结束上报时才更新为 1,异常退出时可用定时任务置为 2。
4.3 三个核心接口:启动、结束、历史统计的请求与响应
后端至少要有三个接口才能把业务流程串起来。启动接口在用户点"开始睡眠"时调用,后端创建一条 status=0 的记录;结束接口在点"结束睡眠"时调用,更新时间和时长;历史统计接口给历史页提供数据。
| 接口 | 路径 | 请求参数 | 返回 |
|---|---|---|---|
| 启动睡眠 | POST /api/sleep/start | userId(token 解析) | 记录 id |
| 结束睡眠 | POST /api/sleep/end | sleepDate, startTime, endTime, awakeCount | 记录 id |
| 历史统计 | GET /api/sleep/history | dateRange, page, size | 记录列表 + 总条数 |
历史统计接口返回的数据结构建议设计成:{ total, records: [ { sleepDate, durationMinutes, deepMinutes, awakeCount } ] }。前端拿到records后,直接按sleepDate排序画柱状图和折线图。这个接口的 SQL 要按日期倒序、分页查询,同时durationMinutes按分钟存,前端算小时时除以 60 保留一位小数即可,不要在数据库里存小时数,小数精度在 SQL 里处理起来很别扭。
5. 常见问题排查:从联调失败到上线前翻车的 5 个坑
这部分是血泪经验。一套源码从"能打开"到"能演示",中间隔着少则十几个多则几十个坑。我挑 5 个最高频的,按"现象→原因→解决"的排查思路写,希望能帮你少走弯路。
5.1 小程序请求一直报 fail:域名校验与本地调试开关
现象:前端点击上报,wx.request的fail回调被触发,控制台提示url not in domain list,或者报request:fail。 原因:微信开发者工具默认开启"校验合法域名",而本地后端地址是http://localhost:8080或局域网 IP,不在微信白名单里——这个校验设计的本意是防止小程序请求任意服务器,但开发阶段它就成了拦路虎。 解决:开发阶段在微信开发者工具的"详情 → 本地设置"里勾选"不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书"。这是开发环境的标准操作,上线时必须换成已备案的 HTTPS 域名,并配好合法域名。如果你用 charles 这类通用抓包工具调试小程序,也要先关掉这个校验,否则流量根本发不出去。
5.2 后端连不上 MySQL:8.0 认证插件与 docker 安装的坑
现象:后端启动时日志报Unable to load authentication plugin 'caching_sha2_password',或者Communications link failure。 原因:MySQL 8.0 默认认证插件是caching_sha2_password,Spring Boot 2.x 内置的 MySQL 驱动(5.1.x 或 6.x)不认识;Communications link failure则多半是连接地址、端口或账号密码配错。 解决:升级驱动到mysql-connector-java 8.x,JDBC URL 加serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true。如果是用 Docker 装的 MySQL,先检查docker ps看容器是否在跑、端口是否映射到宿主机——常见错误是-p 3306:3306没加导致宿主机连不上。
5.3 睡眠时长算成负数:时区与时间字段的类型选择
现象:历史页显示的睡眠时长是负数,比如 -8 小时,或者结束时间比开始时间还早。 原因:这是典型的时区问题。前端传的是"北京时间字符串",后端 JVM 默认时区不是Asia/Shanghai,数据库会话时区也不是,存进去的DATETIME被偏移了几个小时。另一种情况是end_time存成了次日凌晨,但sleep_date还是前一天,跨天计算时长时没加 24 小时。 解决:三处统一。JDBC URL 加serverTimezone=Asia/Shanghai;后端启动参数加-Duser.timezone=Asia/Shanghai;数据库连接会话时区也设成东八区。跨天问题在代码里判断:如果endTime < startTime,说明跨天了,endTime加一天再减startTime。这类"玄学"问题最难排查,建议一上来就把时区配好。
5.4 睡眠记录重复插入:按钮防抖、事务与唯一约束
现象:用户连点两次"结束睡眠",历史页出现两条一模一样的数据。 原因:前端按钮没有禁用,后端也没有幂等检查。两个请求间隔几毫秒到达后端,线程 A 和线程 B 同时查询"今天是否有记录",都查到没有,于是各自插入一条。 解决:三层防护。前端在请求发出后立即禁用按钮;后端在 service 层做幂等检查,查到了就拒绝;数据库层面给(user_id, sleep_date)加唯一索引,作为最后一道兜底。加了唯一索引后,重复插入会抛DuplicateEntry异常,这个异常要捕获并转成"今天已上报"的提示,而不是让用户看到 500。
5.5 真机传感器失灵:权限声明与后台运行限制
现象:模拟器里加速度计一切正常,但用手机预览时onAccelerometerChange不回调,或者息屏后再亮屏就再也拿不到数据。 原因:少了app.json里的permission声明,传感器授权弹窗没有弹出;iOS 微信对后台传感器监听做限制,App 切后台或息屏一段时间后回调自动停止。 解决:补上scope.accelerometer的permission配置,同时告诉用户"息屏后传感器会暂停,建议睡前点开始,起床后立即点结束"。另一个小技巧:真机预览时把开发者工具的"微信真机调试"打开,看console里的传感器回调日志,确认数据是在采集还是根本没触发。
6. 交付前的最后验证:一份可执行的自检清单
毕业设计最怕的不是功能做不完,而是演示前最后一小时才发现某个环节是坏的。下面这份自检清单花半小时就能过一遍,强烈建议答辩前一天晚上按顺序走一次。
6.1 功能验收表:从注册到历史统计
| 功能点 | 操作路径 | 预期结果 |
|---|---|---|
| 注册登录 | 我的页 → 注册 | 能入库,密码不以明文存储 |
| 启动睡眠 | 首页 → 开始睡眠 | 生成 status=0 的记录 |
| 结束睡眠 | 首页 → 结束睡眠 | 生成记录,时长分钟数为正 |
| 重复上报 | 连续点两次结束 | 第二次提示"已上报过" |
| 历史统计 | 历史页 → 下拉刷新 | 列表按日期倒序,时长展示正确 |
| 异常中断 | 杀掉小程序重新进 | 之前进行中的记录自动置为 2 |
6.2 用开发者工具的 Network 面板做接口验证
拿到源码包后,我建议把接口验证当成固定动作。微信开发者工具的 Network 面板能看到每一次wx.request的完整链路:请求地址、请求头、响应状态码、返回体积。打开它,手动触发一次"结束睡眠",然后确认三件事:请求头里Authorization带上了;响应状态码是 200;响应体的data里有记录 id。如果后端报 401 或 400,查看后端控制台的异常堆栈——这个习惯能省掉大量"改前端还是改后端"的无效尝试。你也用 charles 这类工具做更细的抓包分析,但优先用开发者工具自带的,配置成本最低。
6.3 LW 论文里三张必补的图
说明文档和 LW(论文)是答辩打分的重要依据。"功能模块图 + ER 图 + 业务时序图"三张图是必补的。功能模块图画用户、睡眠记录、统计报表三大模块;ER 图画出t_user和t_sleep_record的关系,标注主键外键;业务时序图画"用户点开始 → 前端监听传感器 → 点结束 → 上报后端 → 落库 → 历史页展示"的完整链路。这三张图画完,论文的第三章(系统设计)就有了,也能顺带把数据库表设计的章节补全。
带过几轮毕设项目后我发现一个共性教训:源码能不能跑通,取决于你愿不愿意在拿到包的前两个小时里把环境配好、把数据流走通。这套"睡眠检测小程序"的技术方向不会过时——传感器采集、数据上报、统计分析是小程序开发的经典链路,做一遍的收获比看十篇教程都大。希望帮到你。
本文还有配套的精品资源,点击获取