资讯动态

SSM+Vue+微信小程序健康管理系统源码实战:架构、部署与二次开发

发布时间:2026/9/28 14:21:50 来源:尧图企业网站定制
简介基于微信小程序的健康管理系统实现源码采用Java与SSMSpring、SpringMVC、MyBatis结合Vue前端面向毕业设计、课程设计及前后端分离开发学习者。系统围绕健康数据查看、记录与管理设计后端使用SpringBoot简化配置MySQL 5.7负责数据存储MAVEN管理构建微信小程序承担移动端展示交互结构清晰、便于直接运行和扩展。资源压缩包大小16.61MB共包含1137个文件覆盖vue组件、java后端类、js逻辑、png/svg界面资源、wxml/wxss小程序页面、sql脚本、json配置等核心类别基本具备前后端开发所需素材。项目内附带bat脚本与清楚目录划分可帮助快速完成安装、启动和构建流程并从中理解SSM与Vue的实际整合方式。目前已有256人学习下载适合作为完整项目参考来梳理健康管理功能的实现思路。1. 从一个健康管理小程序的问题说起这套源码到底在做什么把 Java-ssm-vue 基于微信小程序的健康管理系统源码导入 IDEA数据库脚本也执行了小程序一打开还是空白页这种问题我遇到过太多次。所谓健康管理小程序并不是一个单页应用而是三条线同时协作SSM 负责接口和业务规则Vue 管理后台负责用户与数据管理小程序负责用户每天提交体征数据。源码里真正值钱的部分不是页面样式而是登录态、健康档案、体征记录这几条主链路。这篇文章会把表结构、启动步骤、提交链路和高频故障一次性讲完适合课程设计接手源码的新手也适合要基于这套代码做二次开发的工程师直接用。2. SSM Vue 微信小程序三条线怎么协作拿到源码第一步不是跑代码而是先把三端关系看清楚。SSM 是后端底座Vue 管理端和小程序都通过 HTTP 接口访问它小程序面向用户Vue 端面向运营人员。很多人翻车是因为在小程序里直接写 SQL、在 Controller 里拼业务最后数据对不上。2.1 后端 SSM 分层Controller、Service、Mapper 各管一摊SSMSpring SpringMVC MyBatis看起来配置比 Spring Boot 多但每一层都看得到。健康管理系统的正确职责划分是小程序和 Vue 管理端都只跟 Controller 对话Controller 只接收参数、调 Service、返回统一结构Service 里放业务规则比如血压偏高提醒、 BMI 计算、运动建议Mapper 只做 SQL 交换。统一返回结构决定了前端好不好写。源码里一般会有一个 Result 类我的习惯是固定成 code、msg、data 三个字段public class ResultT { private Integer code; private String msg; private T data; public static T ResultT success(T data) { ResultT r new Result(); r.code 0; r.msg ok; r.data data; return r; } public static T ResultT error(String msg) { ResultT r new Result(); r.code 1; r.msg msg; return r; } }这里把 code 为 0 定义为成功小程序端判断的是res.data.code 0而不是 HTTP 状态码。很多二次开发的人把后端返回码和 HTTP 状态码混在一起小程序端判断逻辑就会乱。2.2 Vue 管理后台与 SSM 对接token、CORS、proxyVue 管理端常见的模板是 Element Admin 之类的前后端分离工程。用户登录后后端返回一个 token前端存到 localStorage请求拦截器里每次带上Authorization请求头。健康管理后台一般要展示用户列表、健康档案、体征趋势这些数据全通过/api前缀的接口拿。开发环境最常遇到跨域。我一般不在 SSM 后端强行开 CORS而是在 vue.config.js 里做代理// vue.config.js module.exports { devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }这段配置的意思是Vue 开发服务器收到/api开头的请求时转发到localhost:8080。changeOrigin: true会让后端认为请求来自同源地址省掉跨域拦截。上线后这个代理失效生产环境通常用 Nginx 把/api反代到后端服务。2.3 小程序登录链路code2session 和 openid 怎么换成自己的 session微信小程序登录和普通用户名密码登录不一样。小程序端调用wx.login()拿到一个临时 code后端用这个 code 加上 appid、secret 去微信接口换 openid。openid 是用户在这个小程序里的唯一标识不是用户名也不是手机号。核心代码长这样// WechatLoginController.java PostMapping(/api/wechat/login) public Result login(RequestBody LoginParam param) { // 1. 小程序端 wx.login() 返回的一次性 code String code param.getCode(); // 2. 用 code 换 openid注意 code 只能使用一次5 分钟内有效 String url https://api.weixin.qq.com/sns/jscode2session ?appid wechatConfig.getAppid() secret wechatConfig.getSecret() js_code code grant_typeauthorization_code; String resp restTemplate.getForObject(url, String.class); JSONObject obj JSON.parseObject(resp); String openid obj.getString(openid); // 3. 查库或新建用户健康档案表也在这里做兜底初始化 User user userMapper.findByOpenid(openid); if (user null) { user new User(); user.setOpenid(openid); userMapper.insert(user); } // 4. 生成你自己的会话 token后续请求带上 Authorization String token UUID.randomUUID().toString().replace(-, ); tokenMapper.save(token, user.getId()); return Result.success(token); }这段逻辑里最容易被忽略的是code的时效性。如果用户在小程序端连续调用wx.login()前一个 code 会立即失效后端的登录请求可能报errcode 40163之类的问题。另一点是 openid 必须是唯一索引否则同一个小程序用户换设备后会产生两条用户记录。3. 数据模型健康档案、体征记录、运动饮食怎么落进 MySQL数据模型是健康管理系统的地基。很多源码跑不起来不是因为代码错而是表结构没建对或者 MyBatis 映射和字段对不上。3.1 表关系设计用户、档案、记录怎么拆才不会改到哭常见做法是拆成四张核心表用户表、健康档案表、体征记录表、健康提醒表。用户表只存微信身份健康档案表存姓名、性别、出生日期、身高、体重、过敏史、慢病史体征记录表存血压、血糖、体重、运动、饮食等动态数据健康提醒表存系统生成的提醒事项。档案和用户是一对一记录和用户是一对多。如果是课程设计源码可能还多一张管理员表但这张表不影响主链路。真正要注意的是别把所有体征数据塞进一张大宽表字段超过四十个以后再加一种记录类型就得改表结构。3.2 建表 SQL字段类型、枚举、复合索引一次定清楚以体征记录为例我最常用的表结构是这样CREATE TABLE health_archive ( archive_id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, real_name VARCHAR(32), gender TINYINT COMMENT 1男 0女, birth_date DATE, height_cm DECIMAL(5,1), weight_kg DECIMAL(5,1), allergy VARCHAR(255), chronic_disease VARCHAR(255), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_user_id (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT健康档案表; CREATE TABLE health_record ( record_id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, record_type VARCHAR(20) NOT NULL COMMENT bp/glucose/weight/exercise/diet, record_value VARCHAR(50) NOT NULL COMMENT 118/76、5.2、66.5 等, measure_time DATETIME NOT NULL, remark VARCHAR(255), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_user_time (user_id, measure_time), KEY idx_type (record_type) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT体征记录表; CREATE TABLE health_reminder ( reminder_id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, content VARCHAR(255), remind_time DATETIME, status TINYINT DEFAULT 0 COMMENT 0未完成 1已完成, KEY idx_remind_user (user_id, remind_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT健康提醒表;字段类型上height_cm和weight_kg用 DECIMAL 而不是 FLOAT避免体重 66.6 变成 66.60000000000001 的尴尬。record_value用 VARCHAR 是为了兼容118/76这种带斜杠的表达如果以后要做数值区间统计建议再拆成systolic和diastolic两列而不是解析字符串。记录表一定要建(user_id, measure_time)复合索引。健康管理系统的查询几乎都是先定位用户再按时间排序这个索引能直接命中。record_type单独建索引是因为后台管理端经常按类型筛选。3.3 MyBatis XML 和实体映射的三个关键约定拿到源码后如果发现查询字段全是 null八成是 MyBatis 驼峰映射没开。在mybatis-config.xml里加一句话settings setting namemapUnderscoreToCamelCase valuetrue/ /settings打开后数据库的record_type才能自动映射到实体的recordType字段。SSM 老项目里经常有手写resultMap的写法接管字段映射后更稳但如果表字段统一了下划线风格驼峰映射能省大量重复代码。第二个约定是#{}和${}不要混用。#{}会被 MyBatis 编译成?占位符安全且能防止 SQL 注入${}是字符串替换一般只用在表名、排序字段这种没法参数化的地方。健康管理系统里如果用了${}接收前端传值会被扫描到高危漏洞。第三个约定是插入记录时要拿回自增主键。用方式insert idinsertRecord useGeneratedKeystrue keyPropertyrecordId insert into health_record(user_id, record_type, record_value, measure_time, remark) values(#{userId}, #{recordType}, #{recordValue}, #{measureTime}, #{remark}) /insertuseGeneratedKeystrue会把数据库生成的自增 ID 写回到传入对象的recordId字段上。后续如果要做保存记录后立即生成提醒这个主键就是关联的依据。4. 用源码在本地跑通从导入后端到小程序提交一条血压记录能看懂架构不等于能跑起来。SSM 项目最痛的阶段就是环境匹配JDK 版本、Tomcat 版本、Maven 仓库、数据库驱动任何一个不对都能卡一下午。4.1 导入 IDEA 前的环境准备JDK、Maven 和 Tomcat 版本怎么配SSM 项目基本是 JDK 1.8我一般不建议拿 JDK 17 硬跑。Maven 用 3.6 左右即可本地仓库建议先换成阿里云镜像不然下载 Spring 依赖会很煎熬。Tomcat 用 8.5 最稳和 javax.servlet 命名空间对应部分新版本源码可能改成 Spring Boot 的 skin但标题既然写的是 ssm默认走传统 war 包。命令行验证一遍最快java -version mvn -v如果mvn -v显示的是 JDK 17需要改JAVA_HOME环境变量指向 JDK 8。导入 IDEA 时选择pom.xml作为 Maven Project等依赖下载完后检查 Lombok 插件是否安装SSM 源码里实体类经常用 Lombok 省掉 getter/setter。4.2 四条关键配置数据库连接、appid、secret、文件上传路径SSM 项目一般把配置写在jdbc.properties和wechat.properties里少数源码用application.properties。启动前先改这几项jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/health?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue jdbc.usernameroot jdbc.password123456 wechat.appidwx你的appid wechat.secret你的secret wechat.mockfalse upload.path/data/health/upload这里最值得说的是serverTimezoneAsia/Shanghai。如果漏掉这个参数连接 MySQL 8 时会报时区错误后端接口全部 500。Windows 上上传路径要写成D:/health/upload不要用反斜杠否则文件保存路径解析会出问题。数据库导入方面源码里一般有health.sql或init.sql用 Navicat 执行的时候选择 utf8mb4 字符集。如果你是从零建表直接复制上一章的四张表 SQL 即可不需要额外加外键约束应用层维护一致性更好。4.3 小程序端提交一条血压记录从 wx.request 到 MyBatis 的完整链路小程序端一般有一个utils/request.js封装把 baseURL 和 token 统一管理// utils/request.js const BASE_URL http://localhost:8080 function request(path, method, data) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL path, method: method, data: data, header: { Content-Type: application/json, Authorization: wx.getStorageSync(token) }, success: (res) resolve(res.data), fail: (err) reject(err) }) }) } module.exports { request, BASE_URL }页面里提交一条血压记录const { request } require(../../utils/request) Page({ data: { recordType: bp, value: , time: }, submitForm() { request(/api/record/add, POST, { recordType: this.data.recordType, recordValue: this.data.value, measureTime: this.data.time }).then(res { if (res.code 0) { wx.showToast({ title: 保存成功 }) } }) } })request函数里wx.getStorageSync(token)是登录时后端返回的会话标识每次请求都会带上。如果 token 过期后端需要返回特定 code小程序端根据 code 自动跳转登录页。后端 Controller 接收这条数据RestController RequestMapping(/api/record) public class HealthRecordController { Resource private HealthRecordService healthRecordService; PostMapping(/add) public Result add(RequestBody HealthRecord record) { // userId 从登录态解析不要信任前端传值 Long userId UserContextHolder.get(); healthRecordService.addRecord(userId, record); return Result.success(); } }Service 里做业务规则Service public class HealthRecordServiceImpl implements HealthRecordService { Resource private HealthRecordMapper healthRecordMapper; Override Transactional(rollbackFor Exception.class) public void addRecord(Long userId, HealthRecord record) { record.setUserId(userId); // 简单规则血压超过 140/90 时追加提醒文案 if (bp.equals(record.getRecordType())) { String[] arr record.getRecordValue().split(/); int high Integer.parseInt(arr[0]); int low Integer.parseInt(arr[1]); if (high 140 || low 90) { record.setRemark((record.getRemark() null ? : record.getRemark()) 血压偏高); } } healthRecordMapper.insertRecord(record); } }这里有两个细节userId从UserContextHolder取而不是从前端传防止用户伪造数据往别人头上记Transactional(rollbackFor Exception.class)保证插入记录时如果后面还要更新健康提醒不会一半成功一半失败。5. SSM 健康管理系统的 5 个高频坑现象、原因、解决这套源码的问题基本集中在连接、映射、登录和跨域上。下面五条我按真实频次排序写每一条都按现象到解决给结论。5.1 小程序请求直接连不上后端ERR_CONNECTION_REFUSED现象微信开发者工具里调用接口console 报net::ERR_CONNECTION_REFUSED后端日志没有任何输出。原因最常见的是后端没启动或者后端监听的是127.0.0.1而小程序端访问的是局域网 IP。如果在手机上调试localhost指的是手机自身不是你电脑。解决先用浏览器直接访问http://localhost:8080/api/xxx确认后端可用真实调试时把BASE_URL改成电脑的局域网地址例如http://192.168.31.100:8080。Tomcat 启动监听地址要配置成0.0.0.0只监听 127.0.0.1 的话真机永远连不上。开发者工具右上角详情-本地设置里勾选不校验合法域名本地联调才不会拦截http://请求。5.2 一调用 Mapper 就报 Invalid bound statement现象项目能启动Controller 也能进去但一执行userMapper.findByOpenid()就抛org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)。原因Mapper 接口有了但 mapper XML 没有被 MyBatis 扫描到。很多 SSM 源码把 XML 放在src/main/java/com/xx/mapper/目录下Maven 默认不会把.xml从 java 目录打包进 classes。解决XML 文件统一放到src/main/resources/mapper/目录并在 Spring 配置里声明bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namemapperLocations valueclasspath:mapper/*.xml/ /bean同时确认 XML 里的 namespace 等于接口的全限定名比如接口是com.xxx.mapper.UserMappernamespace 必须是全路径少一个包名都会报同样的错。5.3 MySQL 8 连接总失败驱动版本、时区与认证插件现象启动 Spring 容器时报Unable to load authentication plugin caching_sha2_password或者Communications link failure。原因MySQL 8 默认使用caching_sha2_password认证插件驱动版本太低会不认识另一个常见原因是连接串没有加serverTimezone。解决驱动升级到mysql-connector-java 8.0.x连接串加上serverTimezoneAsia/Shanghai和allowPublicKeyRetrievaltrue。如果你用的是旧驱动干脆降级 MySQL 到 5.7 或者把认证改掉ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码;5.4 登录接口返回的 openid 全是同一个mock 开关没关现象所有用户登录后 openid 都是固定值健康档案数据串到一个人名下。原因源码里为了演示方便通常有一个wechat.mocktrue配置或者后端写死了默认 openid 来绕过微信登录。接手时只改了数据库和端口没改这个开关。解决在配置里把 mock 关掉填上真实的小程序 appid 和 secret。注意真机调试时 appid 必须是小程序后台里开发管理-开发设置的 AppSecret不是游客模式。改完后重启后端并清掉小程序端缓存的 token重新走一次登录。5.5 Vue 管理端报跨域错Access-Control-Allow-Origin 缺失现象Vue 管理端浏览器控制台报No Access-Control-Allow-Origin header is present on the requested resource但用 Postman 调后端是正常的。原因浏览器拦截了非同源请求Postman 不关心同源策略所以只有浏览器环境会暴露。后端如果没有 CORS 过滤器Vue 开发服务器代理又没配置就会这样。解决开发环境按 2.2 节的 vue.config.js 配 proxy如果不想配代理后端加一个过滤器Component public class CorsFilter implements Filter { Override public void doFilter(...) { response.setHeader(Access-Control-Allow-Origin, *); response.setHeader(Access-Control-Allow-Methods, GET, POST, PUT, DELETE); response.setHeader(Access-Control-Allow-Headers, Authorization, Content-Type); chain.doFilter(request, response); } }Access-Control-Allow-Headers里必须带上Authorization否则小程序端和 Vue 端带 token 的请求会被浏览器判为非法请求头。上线前建议把*改成实际域名避免任何站点都能调你的接口。6. 交付前的进阶改造权限、事务和上线检查这套源码能在本地跑通只是开始真正交给用户前我一般会做两件小事统一权限控制和敏感数据脱敏。先检查 Service 层所有写操作方法确保都加了Transactional。这里有个容易忽略的坑事务注解加在私有方法上不会生效因为 Spring 动态代理只拦截通过代理对象调用的公有方法。Service 内部同类调用自己另一个方法时事务同样会失效。我习惯把写入操作独立成一个 Public 方法或者单独拆一个 Service 类。敏感数据脱敏也要做。健康档案里的手机号、身份证、详细病史在 Vue 管理端列表里不能明文全部展示。常见的做法是后端返回前处理一遍手机号只保留前三位和后四位。上线检查可以用一张表格收口检查项操作打包mvn clean package -DskipTests生成 war数据库确认线上库字符集 utf8mb4索引已执行https小程序正式环境要求接口必须是备案过的 HTTPS 域名文件上传上传目录权限和磁盘容量确认日志至少保留接口入参与耗时日志方便排查用户反馈部署时我习惯写一个最小化脚本避免每次手动打 warmvn clean package -DskipTests scp target/health.war rootyour-server:/opt/tomcat/webapps/ ssh rootyour-server systemctl restart tomcat我自己的习惯是每接手一套 SSM 小程序源码第一件事是先把BASE_URL、wechat.mock、数据库密码三个配置抽到独立文件里部署环境只要改这个文件不用进代码。之前有一次就是因为上线时忘了改 BASE_URL真机刷不出列表还被当成后端 bug 排查了很久。早一点把环境配置从业务代码里拆出来能省下后面很多重复踩坑的时间希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑