ARTICLE DETAIL

资讯详情

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

微信小程序+Java远程诊疗系统毕业设计实战

微信小程序+Java远程诊疗系统毕业设计实战 简介本资源是一套面向计算机专业本科生的毕业设计级微信小程序远程在线诊疗系统适用于Java全栈开发学习与课程设计实践解决传统线下问诊流程繁琐、信息管理低效等问题。压缩包含1417个文件总大小39.85MB涵盖151个Java后端业务逻辑与SSM框架代码、208个JS/WXML/WXSS前端小程序页面脚本与样式、153个Vue组件含管理后台界面、231张界面截图与图标资源以及2个SQL建表脚本和完整开题报告、论文、PPT、使用说明等配套文档。已有56人学习下载资源结构清晰小程序端实现用户预约挂号、医生在线问诊与处方管理、管理员多角色权限管控后台基于JavaSpringSpringMVCMyBatis构建MySQL存储核心数据支持浏览器直接访问管理界面。读者可获得从需求分析、数据库设计、前后端分离开发到部署说明的全流程开源实现具备完整复现与二次开发基础。1. 微信小程序Java后端的远程在线诊疗系统毕业设计能跑通、能答辩、能演示的真实落地方案这不是一个“套壳PPT空数据库假接口”的毕业设计模板。我带过三届计算机专业毕设每年都有学生拿着标着“含全套源码”的压缩包来问我“老师为什么小程序点登录就白屏为什么后台启动报错ClassNotFoundException: com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor为什么数据库建表脚本执行完user表里连一条测试数据都没有”——问题根源不在学生而在于市面上大量所谓“免费开源毕业设计”本质是把 Spring Boot 2.3 MyBatis-Plus 3.4 的老项目硬塞进微信小程序前端却完全没处理微信登录态穿透、小程序用户身份与 Java 后端 Session/Token 的双向绑定、医疗场景下问诊记录的事务一致性这三个毕业答辩必被追问的核心链路。这个标题指向的是一套真实可部署、可交互、可演示全流程的远程在线诊疗系统患者用微信小程序挂号、查看医生排班、发起图文问诊医生用 PC 端管理后台基于 Vue3 Element Plus接单、回复、开电子处方Java 后端Spring Boot 2.7.18 MyBatis-Plus 3.5.3.1 Redis 7负责业务编排、JWT 鉴权、问诊消息队列RabbitMQ、处方 PDF 生成MySQL 8.0 存储核心业务数据且所有 SQL 脚本均适配utf8mb4_0900_as_cs排序规则避免微信昵称含 emoji入库乱码。它不是玩具而是按真实医疗 SaaS 系统最小可行单元MVP裁剪出的毕业设计级实现——重点在“能讲清技术选型理由”、“能复现关键流程”、“能解释清楚为什么用 RabbitMQ 而不用轮询”、“能说出微信手机号解密失败时日志里最先看哪三行”。适合需要快速搭建可运行系统、聚焦业务逻辑而非底层协议的学生也适合想用真实项目练手 Java 全栈开发的初级工程师。2. 搭建环境与初始化从零跑通小程序Java后端的最小闭环2.1 后端 Java 环境Spring Boot 2.7.x 是当前毕业设计最稳的选择别碰 Spring Boot 3.x。虽然新但它的 Jakarta EE 9 命名空间jakarta.servlet.*和默认禁用 Hibernate 二级缓存会让很多毕业设计里抄来的Transactional注解失效且 MyBatis-Plus 3.5.x 对它的兼容性文档极不清晰。我们锁定Spring Boot 2.7.182023 年 10 月 LTS 版搭配JDK 17LTSGradle 7.6 原生支持。Maven 依赖核心片段如下!-- pom.xml 关键依赖 -- properties java.version17/java.version spring-boot.version2.7.18/spring-boot.version mybatis-plus.version3.5.3.1/mybatis-plus.version redisson.version3.23.2/redisson.version /properties dependencies !-- Web 核心 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId version${spring-boot.version}/version /dependency !-- MyBatis-Plus注意不是 mybatis-spring-boot-starter-- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version${mybatis-plus.version}/version /dependency !-- Redis用于存储微信 session_key 和临时登录凭证-- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId version${spring-boot.version}/version /dependency !-- RabbitMQ异步处理问诊消息通知-- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-amqp/artifactId version${spring-boot.version}/version /dependency !-- JWT 工具Hutool 封装更轻量比 jjwt 更少依赖冲突-- dependency groupIdcn.hutool/groupId artifactIdhutool-all/artifactId version5.8.22/version /dependency /dependencies提示mybatis-plus-boot-starter是必须项它自动装配了MybatisSqlSessionFactoryBean和分页插件。如果误引入mybatis-spring-boot-starter会导致TableName注解失效UserMapper.selectList(null)返回空集合——这是毕业答辩现场最常翻车的“玄学 Bug”。2.2 数据库初始化MySQL 8.0 utf8mb4_0900_as_cs 是唯一安全选项微信用户昵称含 emoji如 ‍⚕️、❤️MySQL 5.7 默认utf8实际是utf8mb3最多存 3 字节字符emoji 会变?或直接报错Incorrect string value。必须用utf8mb4且排序规则必须是utf8mb4_0900_as_cs大小写敏感、口音敏感否则WHERE nickname 张三可能匹配到张三 带空格或张三 全角空格。建库语句执行前确认 MySQL 8.0-- 创建数据库显式指定字符集和排序规则 CREATE DATABASE IF NOT EXISTS online_medical DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_cs; -- 创建用户并授权生产环境请限制 IP CREATE USER med_user% IDENTIFIED BY Med2024Pass; GRANT ALL PRIVILEGES ON online_medical.* TO med_user%; FLUSH PRIVILEGES;application.yml中 JDBC URL 必须带参数spring: datasource: url: jdbc:mysql://localhost:3306/online_medical?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrueuseSSLfalsezeroDateTimeBehaviorconvertToNull username: med_user password: Med2024Pass注意zeroDateTimeBehaviorconvertToNull是关键。MySQL 8.0 默认严格模式下0000-00-00日期会报错而部分历史医疗数据如未填写出生日期可能含此值。不加此参数UserMapper.selectById(1)会抛DataTruncation异常。2.3 小程序前端用wx.login()wx.getUserProfile()获取用户身份的正确链路微信官方已废弃wx.getUserInfo()必须用wx.getUserProfile()获取昵称头像用wx.login()获取临时登录凭证code。二者不能合并调用必须分两步且顺序固定用户点击“授权登录”按钮 → 触发wx.getUserProfile()→ 获取nickName,avatarUrlwx.getUserProfile()成功回调内立即调用wx.login()→ 获取code将codeencryptedDataiv一起 POST 到后端/api/wx/login接口小程序端关键代码pages/index/index.js// 2.3.1 获取用户信息必须用户主动触发 bindGetUserProfile() { wx.getUserProfile({ desc: 用于完善会员资料, success: (res) { console.log(获取用户信息成功, res); this.setData({ userInfo: res.userInfo }); // ✅ 立即获取 code不能放在 setTimeout 或其他异步里 wx.login({ success: (loginRes) { console.log(获取 code 成功, loginRes.code); // 发送 code、encryptedData、iv 到后端 this.sendLoginRequest(loginRes.code, res.encryptedData, res.iv); }, fail: (err) { console.error(wx.login 失败, err); } }); }, fail: (err) { console.error(getUserProfile 失败, err); } }); }, sendLoginRequest(code, encryptedData, iv) { wx.request({ url: https://your-server.com/api/wx/login, method: POST, data: { code, encryptedData, iv }, success: (res) { if (res.data.code 200) { wx.setStorageSync(token, res.data.data.token); wx.switchTab({ url: /pages/home/home }); } } }); }逻辑说明wx.login()的code有效期 5 分钟且同一code只能使用一次。后端需用此code向微信服务器换取session_key和openid再用session_key解密encryptedData含手机号等敏感信息。encryptedData和iv必须与code同次登录流程中获取跨流程解密必然失败——这是“为什么小程序登录总提示‘解密失败’”的最常见原因。3. 核心业务落地挂号、问诊、处方三大流程的 Java 实现要点3.1 微信手机号解密用WxMaService官方 SDK 安全解密拒绝手写 AES很多毕业设计源码里用javax.crypto.Cipher手写 AES-128-CBC 解密极易出错iv长度不对、session_keybase64 解码失败、PKCS5Padding 与 PKCS7Padding 混淆。必须用微信官方weixin-java-miniappSDK它封装了全部细节。Maven 依赖dependency groupIdcom.github.binarywang/groupId artifactIdweixin-java-miniapp/artifactId version4.5.0/version /dependencySpring Boot 配置类WxMaConfiguration.javaConfiguration public class WxMaConfiguration { Bean public WxMaService wxMaService() { WxMaConfig config new WxMaInMemoryConfig(); config.setAppid(your-miniprogram-appid); // 替换为你的小程序 AppID config.setSecret(your-miniprogram-secret); // 替换为你的小程序 Secret config.setToken(your-token); // 微信服务器校验 Token任意字符串 config.setAesKey(your-aes-key); // 消息加解密 Key32位随机字符串 WxMaService service new WxMaServiceImpl(); service.setWxMaConfig(config); return service; } }解密手机号 ControllerWxLoginController.javaRestController RequestMapping(/api/wx) public class WxLoginController { Autowired private WxMaService wxMaService; PostMapping(/login) public ResultMapString, Object login(RequestBody LoginRequest request) { try { // 1. 用 code 换取 session_key 和 openid WxMaJscode2SessionResult sessionResult wxMaService.getUserService() .getSessionInfo(request.getCode()); String openid sessionResult.getOpenid(); String sessionKey sessionResult.getSessionKey(); // 2. 用 sessionKey 解密手机号encryptedData iv WxMaUserInfo userInfo wxMaService.getUserService() .getUserInfo(sessionKey, request.getEncryptedData(), request.getIv()); // 3. 保存用户若不存在生成 JWT Token User user userService.findOrCreateByOpenid(openid, userInfo.getNickName(), userInfo.getAvatarUrl()); String token JwtUtil.createToken(user.getId(), user.getNickname()); MapString, Object data new HashMap(); data.put(token, token); data.put(userInfo, userInfo); return Result.success(data); } catch (WxErrorException e) { log.error(微信登录失败, e); return Result.fail(登录失败 e.getMessage()); } } }参数说明LoginRequest是自定义 DTO包含codeString、encryptedDataString、ivString。WxMaJscode2SessionResult的getSessionKey()返回的是 base64 编码的 session_keySDK 内部已处理解码切勿手动Base64.getDecoder().decode()否则解密失败。3.2 图文问诊流程用 RabbitMQ 解耦高并发消息通知避免 HTTP 超时患者提交问诊后需实时通知医生。若用 HTTP 同步调用医生端接口当医生在线数多、网络抖动时患者端会长时间等待超时 10s体验极差。正确做法是患者提交 → 后端存入consultation表 → 发送 MQ 消息 → 医生端 WebSocket 或定时拉取消费。实体类Consultation.java关键字段TableLogic // 逻辑删除字段 public class Consultation { TableId(type IdType.ASSIGN_ID) private String id; // 雪花 ID非自增 private Long patientId; // 患者 user_id private Long doctorId; // 指派医生 user_id初始为 null private String content; // 图文内容JSON 格式{text:发烧3天,images:[url1,url2]} TableField(fill FieldFill.INSERT) private LocalDateTime createTime; // 自动填充创建时间 TableField(fill FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; // 自动填充更新时间 private Integer status; // 0-待接诊1-接诊中2-已完成3-已关闭 }发送问诊消息ConsultationService.javaService public class ConsultationService { Autowired private RabbitTemplate rabbitTemplate; Transactional // 保证 DB 写入和 MQ 发送原子性 public void createConsultation(Consultation consultation) { // 1. 保存问诊记录 this.save(consultation); // 2. 发送 MQ 消息Topic Exchange路由键为 consultation.new rabbitTemplate.convertAndSend( medical.exchange, // Exchange 名 consultation.new, // Routing Key consultation, // 消息体 message - { // 设置消息持久化防止 RabbitMQ 重启丢失 message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT); return message; } ); } }避坑点Transactional必须加在createConsultation方法上而非其内部调用的this.save()。否则事务不生效DB 写入成功但 MQ 发送失败导致数据不一致——这是毕业设计答辩时被问“如何保证问诊消息不丢”的标准答案。3.3 电子处方生成用 Apache PDFBox 动态渲染避开 iText 商业授权风险毕业设计用 iText 7 生成 PDF若未购买商业授权在Document document new Document();处会抛UnregisteredVersionException。PDFBox 是 Apache 开源库无授权风险且对中文支持良好。Maven 依赖dependency groupIdorg.apache.pdfbox/groupId artifactIdpdfbox/artifactId version2.0.27/version /dependency生成处方 PDF 的 ServicePrescriptionService.javaService public class PrescriptionService { // 加载中文字体需提前将 simsun.ttc 放入 resources/fonts/ private final PDFont font PDType0Font.load( PDDocument.load(new ClassPathResource(fonts/simsun.ttc).getInputStream()), StandardCharsets.UTF_8 ); public byte[] generatePrescription(Prescription prescription) throws IOException { PDDocument doc new PDDocument(); PDPage page new PDPage(PDRectangle.A4); doc.addPage(page); PDPageContentStream content new PDPageContentStream(doc, page); // 设置字体和字号 content.setFont(font, 12); // 写入标题 content.beginText(); content.newLineAtOffset(50, 750); content.showText(电子处方); content.endText(); // 写入患者信息 content.beginText(); content.newLineAtOffset(50, 720); content.showText(患者姓名 prescription.getPatientName()); content.newLineAtOffset(0, -20); content.showText(就诊日期 DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm).format(prescription.getVisitTime())); content.endText(); // 写入药品列表表格形式 float y 650; for (Drug drug : prescription.getDrugs()) { content.beginText(); content.newLineAtOffset(50, y); content.showText(String.format(• %s × %s %s, drug.getName(), drug.getDose(), drug.getUnit())); content.endText(); y - 20; } content.close(); ByteArrayOutputStream out new ByteArrayOutputStream(); doc.save(out); doc.close(); return out.toByteArray(); } }注意simsun.ttc是 Windows 自带宋体需自行下载并放入src/main/resources/fonts/。若用NotoSansCJKsc-Regular.otfGoogle 开源字体需确保其 license 允许嵌入 PDF。4. 避坑指南毕业设计部署与调试中最常踩的 5 个深坑4.1 现象小程序真机调试报request:fail net::ERR_CONNECTION_REFUSED原因后端服务未启动或application.yml中server.port与小程序请求的域名端口不一致。例如后端server.port8080但小程序wx.requestURL 写成https://localhost:8081/api/xxx。解决确认后端进程ps aux | grep java是否存活小程序project.config.json中setting.networkTimeout.request设为 3000030秒避免超时过短关键本地调试时小程序开发者工具 → 详情 → 本地设置 → 勾选“不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书”否则http://localhost:8080会被拦截。4.2 现象MySQL 执行建表脚本报错Specified key was too long; max key length is 767 bytes原因MySQL 5.7 默认innodb_large_prefixOFF且varchar(255)字段设为索引时utf8mb4 下每个字符占 4 字节255×41020 767。解决登录 MySQL 执行SET GLOBAL innodb_file_format Barracuda; SET GLOBAL innodb_file_per_table ON; SET GLOBAL innodb_large_prefix ON;修改my.cnfLinux或my.iniWindows[mysqld] innodb_file_format Barracuda innodb_file_per_table 1 innodb_large_prefix 1重启 MySQL 服务。4.3 现象mvn clean package报错Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile原因pom.xml中maven.compiler.source和maven.compiler.target未设为 17与 JDK 17 不匹配。解决在pom.xmlproperties中添加maven.compiler.source17/maven.compiler.source maven.compiler.target17/maven.compiler.target maven.compiler.plugin.version3.11.0/maven.compiler.plugin.version4.4 现象医生端 Vue3 后台登录后刷新页面 404路由失效原因Vue Router 使用history模式但 Nginx/Apache 未配置 fallback导致/admin/user路径直接 404。解决Nginx 配置增加location /admin { alias /path/to/dist/; try_files $uri $uri/ /admin/index.html; }注意alias末尾有/且try_files最后指向index.html而非404.html。4.5 现象微信登录后WxMaJscode2SessionResult.getSessionKey()返回 null原因code已过期5分钟或已被使用过一次或appid/secret填错或微信小程序后台未开启“开发管理”中的“扫码登录”权限虽不扫码但该开关影响 session 接口。解决在微信小程序后台 → 开发管理 → 开发者工具 → 确保“扫码登录”开关为开启后端日志打印sessionResult全对象确认是否含errcode字段如40029表示 code 无效血泪经验code必须在wx.login()成功回调中立即获取并发送任何setTimeout、await、Promise.all都可能导致超时。5. 毕业答辩加分技巧三个让老师眼前一亮的实操细节5.1 用TableField(exist false)显式声明 DTO 字段杜绝 MyBatis-Plus 的“黑匣子映射”MyBatis-Plus 默认将User实体类所有字段包括password、salt映射到 SQL若前端传参含password字段updateById()会把它也更新进数据库造成密码被覆盖。正确做法是在User实体类中所有非数据库字段必须显式标注TableField(exist false)。public class User { TableId private Long id; private String username; private String password; // 密码字段不存入数据库 TableField(exist false) // ✅ 关键告诉 MP此字段不对应数据库列 private String newPassword; // 前端传的新密码用于修改密码 TableField(exist false) private String confirmPassword; // 确认密码 TableField(exist false) private String token; // JWT Token内存态不入库 }答辩话术“老师我通过TableField(exist false)显式声明了所有非持久化字段这样既避免了 MyBatis-Plus 的隐式映射风险又让代码意图一目了然。比如newPassword字段只在密码修改接口中使用不会意外写入数据库。”5.2 用RedissonLock实现分布式锁解决医生抢单时的超卖问题多个医生同时看到一条新问诊都去点击“接诊”若无锁控制UPDATE consultation SET doctor_id?, status1 WHERE id? AND status0可能被多个线程同时执行成功导致一个问诊被两个医生接走。用 Redisson 分布式锁Service public class ConsultationService { Autowired private RedissonClient redissonClient; public boolean acceptConsultation(Long consultationId, Long doctorId) { RLock lock redissonClient.getLock(lock:consultation: consultationId); try { // 尝试加锁3秒内未获取到则放弃 if (lock.tryLock(3, 10, TimeUnit.SECONDS)) { // 再次检查状态双重检查 Consultation old this.getById(consultationId); if (old ! null old.getStatus() 0) { old.setDoctorId(doctorId); old.setStatus(1); return this.updateById(old); } } return false; } catch (InterruptedException e) { Thread.currentThread().interrupt(); return false; } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } } }参数说明tryLock(3, 10, TimeUnit.SECONDS)表示最多等待 3 秒获取锁锁自动续期 10 秒。若医生接诊耗时超过 10 秒锁会自动释放避免死锁——这是比synchronized或数据库SELECT ... FOR UPDATE更适合分布式场景的方案。5.3 用ValidNotBlank做后端参数校验替代前端 JS 的“伪校验”很多毕业设计只在小程序onSubmit里用if (!value) { wx.showToast({title:不能为空}) }这毫无意义——用户禁用 JS 或抓包绕过非法数据直击后端。必须用 Spring ValidationPostMapping(/consultation) public ResultString createConsultation(Valid RequestBody ConsultationDTO dto) { consultationService.create(dto); return Result.success(提交成功); } // DTO 类 public class ConsultationDTO { NotBlank(message 问诊内容不能为空) private String content; NotNull(message 患者ID不能为空) private Long patientId; Min(value 1, message 病情等级必须大于0) private Integer severity; }验证方法在application.yml中开启全局校验spring: mvc: throw-exception-if-no-handler-found: true web: resources: add-mappings: false并添加全局异常处理器捕获MethodArgumentNotValidException统一返回Result.fail(参数错误xxx)。答辩时演示用 Postman 发送{content:}后端立刻返回{code:400,msg:参数错误问诊内容不能为空}证明校验有效。我带学生做毕设时最常强调的一句话是“不要追求功能多要追求每一行代码你都能向老师说清它为什么存在、怎么工作、不这么写会怎样。” 这套微信小程序Java 远程诊疗系统从环境搭建、登录链路、核心业务到避坑细节全部按真实交付标准打磨。它不炫技但每一步都经得起追问它不复杂但每个模块都体现工程思维。希望帮到你。本文还有配套的精品资源点击获取
返回列表