尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

微信小程序+MySQL青少年心理健康系统:毕业设计源码与教程

发布时间:2026/9/26 14:10:28

资讯中心
01
ARTICLE

微信小程序+MySQL青少年心理健康系统:毕业设计源码与教程

微信小程序+MySQL青少年心理健康系统:毕业设计源码与教程
简介这份资源是面向高校学生与初学者的微信小程序毕业设计完整项目包以青少年心理健康科普为主题适合用作毕业设计、课程设计或自学练手。项目前端基于微信小程序后端采用SpringBoot/SSM框架数据库为MySQL配套源码、数据库脚本与部署说明代码含注释新手也能看懂。压缩包共1453个文件约38.84MB涵盖png、svg、jpg等图片素材js、vue、java等前后端代码json、wxml、wxss等小程序配置与样式文件以及sql脚本、bat启动脚本和部署文档结构完整、层次清晰。目前已有181人学习下载。项目功能完善、界面美观、操作简单经过严格调试可稳定运行读者可据此掌握小程序页面搭建、接口联调与数据库设计思路并借助部署教程快速跑通服务端与开发者工具导入流程具备较高的参考与复用价值。1. 青少年心理健康科普小程序从毕业设计选题到能跑起来的完整交付青少年心理健康这件事落到技术实现上其实是一个典型的「内容 测评 记录」三件套小程序。我见过太多毕业设计卡在同一个地方选题方向没问题数据库表也画了但真到写代码时发现——量表题目怎么存、测评结果怎么算、科普文章怎么分类、用户匿名记录怎么保护这些细节没人讲清楚。这个标题里的「微信小程序 数据库 源码 教程」组合本质上要解决的就是一个学生或者刚接触小程序开发的工程师能不能在两周内从零把这个系统跑起来并且数据库设计经得起答辩老师的追问。适合的人群很明确正在做计算机毕业设计的学生、想练手微信小程序全栈开发的新手、以及需要一套心理健康类小程序基础框架的开发者。接下来我会按「数据库怎么设计 → 小程序端怎么搭 → 测评逻辑怎么写 → 踩过哪些坑」的顺序把每个环节的可复现步骤和参数选择讲透。2. 数据库设计从量表题目到匿名记录的六张核心表2.1 为什么选 MySQL 而不是 SQLite 或云数据库毕业设计场景下数据库选型的第一原则是「答辩时能演示换电脑能跑起来」。SQLite 虽然零配置但多端并发写入时容易锁库而且答辩老师看到.db文件往往觉得「不够正式」。云数据库比如微信云开发自带的 JSON 数据库上手快但导出和迁移麻烦查复杂关联时也不如 SQL 直观。MySQL 8.0 是稳妥选择学校机房大概率装好了网上安装教程一抓一大把mysqldump导出 SQL 文件后换台电脑导入就能继续用。我一般会建议用 Docker 跑 MySQL避免「在我电脑上好好的」这种玄学问题。下面这条命令启动一个带密码的 MySQL 8.0 容器端口映射到本机 3306数据卷挂到当前目录下的mysql_data文件夹删容器不丢数据docker run -d \ --name mental-health-mysql \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDRoot123456 \ -e MYSQL_DATABASEteen_mental_health \ -v $(pwd)/mysql_data:/var/lib/mysql \ mysql:8.0 \ --character-set-serverutf8mb4 \ --collation-serverutf8mb4_unicode_ci参数说明MYSQL_DATABASE会自动建库省去手动CREATE DATABASEutf8mb4字符集必须指定否则 emoji 和部分生僻字会报错-v挂载保证容器重启后数据还在。如果你用本机安装的 MySQL把my.cnf里的character-set-server改成utf8mb4效果一样。2.2 六张核心表的字段设计与建表语句这个系统的数据关系不复杂但有几个容易设计错的地方。我拆成六张表用户表、科普文章表、文章分类表、量表题目表、测评记录表、匿名留言表。先看建表语句再逐条解释为什么这么设计。-- 用户表只存微信 openid 和基础信息不存敏感身份 CREATE TABLE user ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, openid VARCHAR(64) NOT NULL COMMENT 微信唯一标识, nickname VARCHAR(64) DEFAULT 匿名用户, avatar_url VARCHAR(512) DEFAULT , created_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_openid (openid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 文章分类表支持一级分类方便小程序端 tab 切换 CREATE TABLE article_category ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, name VARCHAR(32) NOT NULL COMMENT 如情绪管理、人际交往, sort_order INT DEFAULT 0, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 科普文章表content 用 TEXT摘要单独存 CREATE TABLE article ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, category_id INT UNSIGNED NOT NULL, title VARCHAR(128) NOT NULL, summary VARCHAR(256) DEFAULT , content TEXT NOT NULL, cover_url VARCHAR(512) DEFAULT , view_count INT UNSIGNED DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_category (category_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 量表题目表options 存 JSON避免多表关联 CREATE TABLE scale_question ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, scale_type VARCHAR(32) NOT NULL COMMENT 如PHQ-9、GAD-7, question_text VARCHAR(512) NOT NULL, options JSON NOT NULL COMMENT [{label:完全不会,score:0},...], sort_order INT DEFAULT 0, PRIMARY KEY (id), KEY idx_scale_type (scale_type) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 测评记录表answers 存 JSONresult_score 冗余存储方便统计 CREATE TABLE assessment_record ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, user_id INT UNSIGNED NOT NULL, scale_type VARCHAR(32) NOT NULL, answers JSON NOT NULL COMMENT {1:0,2:1,...}, result_score INT NOT NULL, result_level VARCHAR(16) NOT NULL COMMENT 正常/轻度/中度/重度, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_scale (user_id, scale_type) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 匿名留言表不关联 user_id只存设备指纹哈希 CREATE TABLE anonymous_message ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, device_hash VARCHAR(64) NOT NULL COMMENT 设备指纹哈希用于防刷, content VARCHAR(1024) NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_device (device_hash) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;逻辑说明scale_question的options用 JSON 类型而不是单独建选项表是因为量表题目数量固定、选项结构统一JSON 查询在 MySQL 8.0 里已经够用省去一次 JOIN。assessment_record的answers存 JSON 是为了保留原始作答result_score和result_level冗余存储是为了列表页不用每次重新计算。anonymous_message不存user_id是刻意设计——匿名就要彻底只留设备哈希做防刷答辩时这一点可以展开讲隐私保护思路。2.3 初始化数据量表题目和分类的插入脚本建完表要灌入基础数据否则小程序端打开是空的。PHQ-9 和 GAD-7 是青少年心理健康筛查里最常用的两个量表各 9 题和 7 题选项都是四级评分。下面这段 SQL 插入 PHQ-9 的前三题作为示例完整版按同样格式补全即可INSERT INTO article_category (name, sort_order) VALUES (情绪管理, 1), (人际交往, 2), (学业压力, 3), (自我认知, 4); INSERT INTO scale_question (scale_type, question_text, options, sort_order) VALUES (PHQ-9, 做事时提不起劲或没有兴趣, [{label:完全不会,score:0},{label:好几天,score:1},{label:一半以上时间,score:2},{label:几乎每天,score:3}], 1), (PHQ-9, 感到心情低落、沮丧或绝望, [{label:完全不会,score:0},{label:好几天,score:1},{label:一半以上时间,score:2},{label:几乎每天,score:3}], 2), (PHQ-9, 入睡困难、睡不安稳或睡眠过多, [{label:完全不会,score:0},{label:好几天,score:1},{label:一半以上时间,score:2},{label:几乎每天,score:3}], 3);参数说明sort_order控制题目顺序不要依赖自增 ID 排序因为后期可能插入新题。options里的score是计算总分的关键PHQ-9 总分 0-27分级标准是 0-4 正常、5-9 轻度、10-14 中度、15-27 重度。GAD-7 总分 0-21分级是 0-4 正常、5-9 轻度、10-14 中度、15-21 重度。这些阈值要写死在代码里还是存数据库我建议存代码常量因为分级标准相对固定存数据库反而增加查询开销。3. 微信小程序端搭建从项目初始化到文章列表渲染3.1 项目初始化和目录结构微信开发者工具新建项目时AppID 可以先用测试号后端接口地址填http://localhost:3000但要在开发者工具里勾选「不校验合法域名」。目录结构我习惯这样分miniprogram/ ├── pages/ │ ├── index/ # 首页文章分类 推荐阅读 │ ├── article/ # 文章详情 │ ├── scale/ # 量表列表 │ ├── assessment/ # 答题页 │ ├── result/ # 测评结果 │ └── message/ # 匿名留言 ├── utils/ │ ├── request.js # 封装 wx.request │ └── auth.js # 登录态管理 ├── app.js ├── app.json └── app.wxssapp.json里要配置tabBar至少放「首页」「测评」「我的」三个 tab。request.js封装统一加 token 和错误处理避免每个页面重复写wx.request。3.2 封装 wx.request 和登录态管理小程序端所有请求都要带登录态但wx.login换取的code只能用一次所以流程是启动时调wx.login拿 code → 发给后端换自定义 token → 存wx.setStorageSync→ 后续请求带 token。下面是对应的封装代码// utils/request.js const BASE_URL http://localhost:3000; function request(options) { const token wx.getStorageSync(token); return new Promise((resolve, reject) { wx.request({ url: BASE_URL options.url, method: options.method || GET, data: options.data || {}, header: { Content-Type: application/json, Authorization: token ? Bearer ${token} : }, success(res) { if (res.statusCode 401) { // token 过期重新登录 wx.removeStorageSync(token); wx.showToast({ title: 请重新进入小程序, icon: none }); reject(new Error(unauthorized)); return; } if (res.data.code 0) { resolve(res.data.data); } else { wx.showToast({ title: res.data.msg || 请求失败, icon: none }); reject(new Error(res.data.msg)); } }, fail(err) { wx.showToast({ title: 网络异常, icon: none }); reject(err); } }); }); } module.exports { request };逻辑说明Authorization头用Bearer格式是常见做法后端解析时取Bearer后面的部分。res.data.code 0是约定好的成功码具体值可以改但前后端要一致。401 处理里没有自动重登是因为重登后原请求需要重发逻辑复杂毕业设计场景下提示用户重新进入更简单可靠。3.3 文章列表页的数据绑定和分页加载首页文章列表用scroll-view或页面自带滚动都行关键是分页参数。我一般用page和pageSize两个参数pageSize设 10触底时page 1再请求。下面是对应页面的 JS 和 WXML 核心片段// pages/index/index.js const { request } require(../../utils/request); Page({ data: { articles: [], page: 1, hasMore: true, loading: false }, onLoad() { this.loadArticles(); }, async loadArticles() { if (this.data.loading || !this.data.hasMore) return; this.setData({ loading: true }); try { const list await request({ url: /api/articles?page${this.data.page}pageSize10 }); this.setData({ articles: this.data.articles.concat(list), page: this.data.page 1, hasMore: list.length 10, loading: false }); } catch (e) { this.setData({ loading: false }); } }, onReachBottom() { this.loadArticles(); } });参数说明hasMore判断依据是返回条数是否等于pageSize等于说明可能还有下一页小于说明到底了。loading锁防止触底时重复请求。concat而不是直接赋值是为了保留已加载的数据。后端接口返回的list直接是数组如果后端返回{ list: [], total: 100 }这里要改成list.list。4. 测评逻辑实现量表计分、结果分级和前后端职责划分4.1 计分逻辑放在前端还是后端这是个容易纠结的点。放前端答题完立刻出结果体验好但用户改答案或伪造请求就绕过了放后端安全但多一次网络请求。我的做法是前端算一遍用于即时展示后端算一遍用于存库两边用同一套分级阈值。这样既保证体验又保证数据可信。后端算的时候不信任前端传的分数只接收answers原始作答重新计算。4.2 前端答题页的状态管理和提交答题页要记录每道题的选择用对象存{ questionId: score }。下面是一个简化的答题页逻辑// pages/assessment/assessment.js const { request } require(../../utils/request); Page({ data: { questions: [], answers: {}, currentIndex: 0, scaleType: PHQ-9 }, onLoad(options) { this.setData({ scaleType: options.scaleType || PHQ-9 }); this.loadQuestions(); }, async loadQuestions() { const questions await request({ url: /api/scale/questions?scaleType${this.data.scaleType} }); this.setData({ questions }); }, selectOption(e) { const { questionId, score } e.currentTarget.dataset; const answers { ...this.data.answers, [questionId]: score }; this.setData({ answers }); // 自动跳下一题 if (this.data.currentIndex this.data.questions.length - 1) { this.setData({ currentIndex: this.data.currentIndex 1 }); } }, async submit() { const { answers, scaleType } this.data; if (Object.keys(answers).length this.data.questions.length) { wx.showToast({ title: 还有题目未作答, icon: none }); return; } const result await request({ url: /api/assessment/submit, method: POST, data: { scaleType, answers } }); wx.redirectTo({ url: /pages/result/result?score${result.score}level${result.level} }); } });逻辑说明answers用对象而不是数组是因为题目 ID 可能不连续对象更灵活。selectOption里自动跳下一题是常见交互但最后一题不跳留给用户点提交。提交前校验答题数量防止漏答。redirectTo而不是navigateTo是因为结果页不需要返回答题页。4.3 后端计分接口和分级阈值后端收到answers后从数据库查出该量表的题目和选项按questionId匹配score累加再根据总分查分级表。下面是对应的 Node.js 伪代码// 后端计分逻辑 async function calculateScore(scaleType, answers) { const questions await db.query( SELECT id, options FROM scale_question WHERE scale_type ?, [scaleType] ); let total 0; for (const q of questions) { const selectedScore answers[q.id]; if (selectedScore undefined) { throw new Error(题目 ${q.id} 未作答); } total selectedScore; } const level getLevel(scaleType, total); return { score: total, level }; } function getLevel(scaleType, score) { if (scaleType PHQ-9) { if (score 4) return 正常; if (score 9) return 轻度; if (score 14) return 中度; return 重度; } if (scaleType GAD-7) { if (score 4) return 正常; if (score 9) return 轻度; if (score 14) return 中度; return 重度; } return 未知; }参数说明answers的 key 是题目 IDvalue 是选项分值。getLevel的阈值写死在代码里如果后期要调整分级标准改这里就行。注意 PHQ-9 和 GAD-7 的分级阈值恰好一样但不要因此合并逻辑因为未来可能加其他量表。5. 避坑与排查毕业设计交付前最容易翻车的五个地方5.1 现象小程序真机预览时请求全部失败开发者工具却正常原因开发者工具默认不校验合法域名但真机预览会校验。你的后端如果是http://localhost:3000手机根本访问不到而且微信要求必须 HTTPS。解决毕业设计演示阶段可以在微信公众平台配置「开发环境不校验请求域名」但真机调试时手机和电脑要在同一局域网后端地址改成电脑的局域网 IP比如http://192.168.1.100:3000。如果答辩要求正式环境需要备案域名和 HTTPS 证书这个周期较长要提前准备。5.2 现象量表题目选项的 JSON 在数据库里存进去后取出来变成字符串原因MySQL 的 JSON 类型在 Node.js 驱动里默认返回字符串需要手动JSON.parse。或者建表时用了TEXT而不是JSON。解决确认建表语句里options字段类型是JSON。查询后在代码里判断typeof options string就JSON.parse。更稳妥的做法是在数据库连接配置里加typeCast处理但毕业设计场景下手动 parse 更直观。5.3 现象测评提交后结果页显示「undefined」原因后端返回的数据结构和前端取值不一致。比如后端返回{ code: 0, data: { score: 10, level: 轻度 } }前端request封装里已经取了res.data.data结果页又取result.data.score多了一层。解决统一约定request返回的就是业务数据本身结果页直接用result.score。调试时在request的resolve前加console.log(res.data)看清楚结构再取值。5.4 现象匿名留言功能被刷屏同一设备反复提交原因device_hash生成逻辑太简单或者根本没做频率限制。解决device_hash用wx.getSystemInfo的多个字段拼接后哈希不要只用openid匿名场景不该拿 openid。后端加频率限制同一device_hash一分钟内只能提交一条用 Redis 或内存缓存都行。毕业设计用内存 Map 就够注意重启后清空。5.5 现象换电脑部署时数据库导入报错「Unknown character set: utf8mb4」原因目标电脑的 MySQL 版本低于 5.5.3不支持 utf8mb4。或者导出 SQL 时没有指定字符集。解决确认 MySQL 版本8.0 肯定支持。导出时用mysqldump --default-character-setutf8mb4。导入前先SET NAMES utf8mb4;。如果目标环境实在不支持降级用utf8但 emoji 会丢量表题目里一般没有 emoji影响不大。6. 让答辩加分的两个进阶技巧数据可视化和隐私保护说明6.1 用 ECharts 在小程序里画测评趋势图毕业设计如果只展示单次测评结果显得单薄。加一个「历史测评趋势」折线图答辩时很加分。微信小程序用 ECharts 需要引入ec-canvas组件核心代码如下// pages/result/result.js 中初始化图表 import * as echarts from ../../components/ec-canvas/echarts; function initChart(canvas, width, height, dpr) { const chart echarts.init(canvas, null, { width, height, devicePixelRatio: dpr }); canvas.setChart(chart); chart.setOption({ xAxis: { type: category, data: [第1次, 第2次, 第3次] }, yAxis: { type: value, min: 0, max: 27 }, series: [{ data: [8, 12, 6], type: line, smooth: true, markLine: { data: [{ yAxis: 10, name: 中度阈值 }], lineStyle: { color: #f5222d, type: dashed } } }] }); return chart; }参数说明markLine标出中度阈值线让趋势图有临床参考意义。数据从后端/api/assessment/history?scaleTypePHQ-9获取按时间排序。注意ec-canvas组件需要单独下载放到components目录app.json里不用注册页面 JSON 里注册即可。6.2 隐私保护说明怎么写进答辩 PPT青少年心理健康数据敏感答辩老师大概率会问「你怎么保护隐私」。我的经验是准备三句话第一用户表只存 openid 不存手机号和真实姓名第二测评记录和匿名留言不关联真实身份匿名留言连 user_id 都不存第三所有接口鉴权用 tokentoken 过期时间设 2 小时敏感操作二次校验。这三条写进 PPT 的一页比堆技术名词管用。最后说个血泪教训我当年做类似系统时把量表分级阈值写在了前端后端没校验结果答辩演示时老师问「我改一下请求分数会怎样」当场翻车。后来改成前后端各算一遍前端算的只用于展示后端算的才入库。这个习惯保持到现在——凡是涉及结果判定的逻辑永远不要只信客户端。希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。