简介:这是一套面向计算机专业本科生的毕业设计级微信小程序实战项目,聚焦睡眠健康管理场景,提供从需求分析、前后端开发到部署上线的完整技术闭环。资源包含基于uniapp或原生小程序框架开发的前端代码、Java/PHP后端服务及MySQL 5.7+数据库设计,覆盖助眠音乐控制、睡眠数据可视化记录、睡眠知识问答与个人信息管理四大核心模块,并配套开题报告、设计文档与答辩PPT等学术材料。压缩包共110个文件,含53张界面截图(png)、14个逻辑脚本(js)、13个配置文件(json)、11个样式文件(wxss)及10个页面结构文件(wxml),辅以2份Word文档、2段教学视频(mp4)和1个动态加载动画(gif),整体大小14.63MB。目前已有169人学习下载,读者可直接导入HBuilder X或微信开发者工具运行调试,快速掌握小程序+MySQL全栈开发流程与毕业论文撰写规范。
1. 睡眠检测小程序不是健康数据看板,而是闭环行为干预系统
很多开发者拿到「睡眠检测小程序」这个标题,第一反应是做个带时间选择器和打卡按钮的静态页面,再连个 MySQL 存几条“23:05 入睡”“07:18 起床”。但真实场景里,用户凌晨两点点开小程序,看到“今日睡眠质量:差(深度睡眠仅1.2h)”,下一秒就切出微信刷短视频——数据没触发任何动作,检测就失去了意义。这个毕业设计源码包的价值,恰恰在于它用最小可行结构实现了「感知→反馈→引导」闭环:前端通过手机加速度传感器粗粒度识别体动频次(非医疗级,但符合学生项目边界),后端用 MySQL 建模睡眠阶段过渡规律(比如连续10分钟低频体动+心率区间匹配=浅睡期),再通过小程序模板消息推送轻量干预建议(“您昨晚浅睡偏多,今晚可尝试睡前1小时关闭蓝光屏幕”)。它面向的是能独立部署、理解数据流向、并能基于业务逻辑调整阈值参数的 IT 类毕业生,不是只懂拖拽生成器的零基础学员。
2. 用 wx.getAccelerometer 获取体动特征,而非调用未授权的健康平台API
小程序无法直接读取手机内置的睡眠监测芯片(如华为 TruSleep、苹果 HealthKit),但可通过wx.getAccelerometerAPI 以 50ms 频率采集加速度三轴原始数据。毕业设计源码中采用滑动窗口法处理该数据流,这是兼顾性能与有效性的常见做法。
2.1 加速度数据采集与本地降噪
小程序端在用户点击“开始检测”后启动监听:
// pages/sleep/start.js Page({ data: { isMonitoring: false, rawData: [] }, startMonitor() { const that = this; wx.startAccelerometer({ interval: 'game', // 最高采样频率,约50ms/次 success: () => { that.setData({ isMonitoring: true }); wx.onAccelerometerChange((res) => { // 只保留z轴(垂直方向)数据,人体平躺时z轴变化最敏感 const zValue = Math.abs(res.z); // 滑动窗口:保留最近200个点(约10秒),超长则截断 const newRaw = [...that.data.rawData.slice(-199), zValue]; that.setData({ rawData: newRaw }); }); } }); } });提示:
interval: 'game'是关键参数,'ui'或'normal'模式下采样间隔过长(200ms+),无法捕捉翻身等短时体动;但需注意wx.onAccelerometerChange在后台会被系统暂停,因此必须要求用户保持小程序前台运行。
2.2 服务端用 MySQL 计算体动活跃度指标
前端每30秒将rawData数组通过 POST 发送至/api/sleep/upload接口,后端(Node.js + Express)解析后存入 MySQL 表sleep_raw_data:
CREATE TABLE `sleep_raw_data` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `user_id` VARCHAR(32) NOT NULL COMMENT '微信openid', `session_id` VARCHAR(64) NOT NULL COMMENT '本次检测会话唯一标识', `timestamp` DATETIME DEFAULT CURRENT_TIMESTAMP, `z_values` JSON NOT NULL COMMENT 'JSON数组,存储30秒内所有z轴绝对值', PRIMARY KEY (`id`), INDEX `idx_user_session` (`user_id`, `session_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;注意:
z_values字段使用 JSON 类型,而非拆成多行记录,是因为学生项目需控制表数量与查询复杂度;实际生产环境应拆为子表并建时间分区。
服务端计算逻辑(简化版):
// routes/sleep.js app.post('/api/sleep/upload', async (req, res) => { const { userId, sessionId, zValues } = req.body; // 计算30秒内“显著体动次数”:z值 > 0.3g 的连续段数量(过滤微小抖动) const threshold = 0.3; let movementCount = 0; let inMovement = false; for (let i = 0; i < zValues.length; i++) { if (zValues[i] > threshold && !inMovement) { movementCount++; inMovement = true; } else if (zValues[i] <= threshold) { inMovement = false; } } // 插入汇总指标到 sleep_summary 表 await db.query( 'INSERT INTO sleep_summary (user_id, session_id, timestamp, movement_count) VALUES (?, ?, NOW(), ?)', [userId, sessionId, movementCount] ); res.json({ code: 0 }); });该movement_count就是后续判断“入睡潜伏期”“夜间觉醒次数”的核心输入。MySQL 中不进行实时复杂计算,只做轻量聚合,符合学生项目对数据库能力的合理预期。
3. MySQL 设计睡眠阶段状态机,用触发器自动标记阶段跃迁
单纯存储“每30秒体动次数”无法反映睡眠结构。毕业设计源码通过在 MySQL 中构建有限状态机(FSM),将连续时间段映射为“清醒→浅睡→深睡→REM”的粗略阶段,这是比纯前端判断更可靠的方案——状态持久化后,可回溯任意时刻状态,支撑“历史睡眠报告”功能。
3.1 状态定义与状态转移规则
在sleep_stage_log表中定义四类状态及跃迁条件:
| 状态码 | 名称 | 触发条件(基于前3分钟 movement_count 均值) | 持续时间要求 |
|---|---|---|---|
| 0 | 清醒 | > 8 次/分钟 | — |
| 1 | 浅睡 | 3~8 次/分钟 | ≥ 5 分钟 |
| 2 | 深睡 | ≤ 2 次/分钟 | ≥ 10 分钟 |
| 3 | REM | 体动频次回升但低于清醒阈值(4~6次/分钟)+ 心率区间匹配 | ≥ 3 分钟 |
注意:心率数据在本项目中由用户手动录入(模拟),实际可接入微信运动接口(需用户授权),但毕业设计未强制依赖外部API,降低部署门槛。
3.2 用 MySQL 触发器实现状态自动更新
当新记录插入sleep_summary表时,触发器检查最近数据流并更新当前会话的最新状态:
DELIMITER $$ CREATE TRIGGER update_sleep_stage AFTER INSERT ON sleep_summary FOR EACH ROW BEGIN DECLARE avg_mov DECIMAL(5,2); DECLARE last_stage TINYINT; DECLARE current_time DATETIME; SET current_time = NEW.timestamp; -- 计算前3分钟平均体动次数(单位:次/分钟) SELECT IFNULL(AVG(movement_count) * 2, 0) INTO avg_mov FROM sleep_summary WHERE session_id = NEW.session_id AND timestamp BETWEEN DATE_SUB(current_time, INTERVAL 3 MINUTE) AND current_time; -- 获取当前会话最新状态 SELECT COALESCE(stage_code, 0) INTO last_stage FROM sleep_stage_log WHERE session_id = NEW.session_id ORDER BY timestamp DESC LIMIT 1; -- 状态跃迁逻辑(简化版) IF avg_mov > 8 THEN IF last_stage != 0 THEN INSERT INTO sleep_stage_log (session_id, stage_code, start_time, end_time) VALUES (NEW.session_id, 0, current_time, NULL); END IF; ELSEIF avg_mov BETWEEN 3 AND 8 THEN IF last_stage != 1 THEN -- 结束上一状态 UPDATE sleep_stage_log SET end_time = current_time WHERE session_id = NEW.session_id AND end_time IS NULL; -- 开启新状态 INSERT INTO sleep_stage_log (session_id, stage_code, start_time, end_time) VALUES (NEW.session_id, 1, current_time, NULL); END IF; END IF; END$$ DELIMITER ;该触发器不处理深睡/REM 的完整判定(需结合心率),但已覆盖主要跃迁路径。学生可在此基础上扩展heart_rate字段和更精细条件,而无需改动应用层代码。
3.3 查询某次会话的完整睡眠阶段序列
前端请求/api/sleep/report?sessionId=xxx时,后端执行:
SELECT stage_code, TIME_FORMAT(TIMEDIFF(end_time, start_time), '%H:%i:%s') AS duration, COUNT(*) AS segment_count FROM sleep_stage_log WHERE session_id = 'xxx' AND end_time IS NOT NULL GROUP BY stage_code ORDER BY MIN(start_time);返回结果直接用于绘制睡眠周期图(如 ECharts 柱状堆叠图),避免前端做状态拼接。
4. 前后端分离下的 token 安全传递与会话绑定策略
小程序用户身份由微信code换取openid后确立,但毕业设计源码中未使用 JWT 或 Redis Session,而是采用 MySQL 表user_session实现轻量会话管理——这既满足毕业设计对“可读性”和“无额外中间件依赖”的要求,又规避了wx.login过期导致的频繁重登问题。
4.1 登录流程:code 换 openid 后写入会话表
小程序端获取code后提交至后端:
// utils/login.js export async function login() { const { code } = await wx.login(); const res = await wx.request({ url: 'https://your-domain.com/api/auth/login', method: 'POST', data: { code } }); if (res.data.token) { wx.setStorageSync('auth_token', res.data.token); // 本地缓存 } }后端(Node.js)处理:
// routes/auth.js app.post('/api/auth/login', async (req, res) => { const { code } = req.body; // 调用微信接口换取 openid(需配置 appid & secret) const wxRes = await axios.get( `https://api.weixin.qq.com/sns/jscode2session?appid=${APPID}&secret=${SECRET}&js_code=${code}&grant_type=authorization_code` ); const { openid, session_key } = wxRes.data; // 生成32位随机token(非JWT,无签名,仅作会话索引) const token = crypto.randomBytes(16).toString('hex'); // 写入 user_session 表,有效期24小时 await db.query( 'INSERT INTO user_session (token, openid, expires_at) VALUES (?, ?, DATE_ADD(NOW(), INTERVAL 24 HOUR))', [token, openid, null] ); res.json({ token }); });对应 MySQL 表结构:
CREATE TABLE `user_session` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `token` CHAR(32) NOT NULL UNIQUE, `openid` VARCHAR(64) NOT NULL, `expires_at` DATETIME NOT NULL, `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `idx_openid_expire` (`openid`, `expires_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;4.2 接口鉴权:每次请求校验 token 有效性
所有受保护接口(如/api/sleep/upload)前置中间件:
function authMiddleware(req, res, next) { const token = req.headers.authorization?.replace('Bearer ', ''); if (!token) return res.status(401).json({ code: 401, msg: 'Missing token' }); db.query( 'SELECT openid FROM user_session WHERE token = ? AND expires_at > NOW()', [token], (err, results) => { if (err || results.length === 0) { return res.status(401).json({ code: 401, msg: 'Invalid or expired token' }); } req.userOpenid = results[0].openid; next(); } ); }提示:此方案不依赖 Redis,所有状态落盘 MySQL,适合学生部署在单台 CentOS 7.9 服务器(如阿里云 ECS);但需注意
user_session表定期清理,可在 crontab 中添加:# 每日凌晨2点删除过期会话 0 2 * * * mysql -u root -p'password' your_db -e "DELETE FROM user_session WHERE expires_at < NOW();"
5. 说明文档(LW)不是操作手册,而是部署决策日志
毕业设计中的“说明文档(LW)”常被误解为功能列表或截图堆砌,但高质量的 LW 应记录每个技术选型背后的约束与权衡。源码包中的 LW 文档包含以下不可省略的章节,它们直接决定部署成功率。
5.1 MySQL 版本与字符集兼容性声明
明确标注测试环境为MySQL 5.7.32(非 8.0+),原因如下:
| 项目 | MySQL 5.7.32 | MySQL 8.0+ | 选型理由 |
|---|---|---|---|
| JSON 支持 | ✅ 基础 JSON 函数(JSON_CONTAINS) | ✅ 更多函数(JSON_TABLE) | 项目仅需JSON_CONTAINS判断字段存在性 |
| 默认字符集 | latin1 | utf8mb4 | 避免建表时因字符集不一致报错,学生可手动执行ALTER DATABASE your_db CHARACTER SET = utf8mb4 |
| 系统变量名 | max_allowed_packet | 同名但默认值更大 | 上传大体积z_valuesJSON 时需确认该值 ≥ 64M |
注意:若在 CentOS 7.9 上安装 MySQL,官方推荐使用
mysql57-community-release-el7-11.noarch.rpm包,而非mysql80-community-release-el7-6.noarch.rpm,避免触发ONLY_FULL_GROUP_BY严格模式导致聚合查询失败。
5.2 小程序app.json中的关键配置项说明
LW 文档需逐条解释app.json中影响上线的字段,例如:
{ "permission": { "scope.userFuzzyLocation": { "desc": "用于获取用户模糊地理位置(非必需,本项目未使用)" }, "scope.record": { "desc": "【必须开启】用于调用 wx.startAccelerometer" } }, "requiredPrivateInfos": ["accelerometer"], "sitemapLocation": "sitemap.json" }"requiredPrivateInfos"是微信 2023 年起强制要求的字段,漏填会导致真机调试时wx.startAccelerometer报错fail permission denied;"sitemap.json"必须存在且内容为{ "desc": "all" },否则小程序无法通过微信审核(即使只是毕业设计演示)。
5.3 部署时config.js的 3 个必改参数
后端config.js中以下参数必须人工修改,LW 文档用表格强调:
| 参数名 | 示例值 | 修改说明 |
|---|---|---|
DB_HOST | 127.0.0.1 | 若 MySQL 与 Node.js 不在同一机器,需改为内网 IP(禁止填localhost) |
WX_APPID | wx1234567890abcdef | 微信公众平台申请的小程序 AppID,不是公众号 AppID |
STATIC_URL | https://your-domain.com | 小程序中图片/音频资源的 CDN 域名,若用本地路径(/static/xxx.png)需确保 Nginx 已配置静态文件路由 |
部署者只需按此表修改,即可跳过 80% 的“启动报错”问题。LW 的价值,正在于把“别人踩过的坑”变成“你抄作业的 checklist”。
验证 MySQL 是否正确加载睡眠阶段数据:执行SELECT * FROM sleep_stage_log WHERE session_id = 'test_001' ORDER BY start_time;,应返回至少 3 条不同stage_code的连续记录,且end_time字段非空。
本文还有配套的精品资源,点击获取