SpringBoot后端+Vue前端的医疗服务平台源码包(含数据库脚本与部署指南)

该文章已生成可运行项目,

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:提供一套可直接运行的智慧医疗平台完整工程,后端用SpringBoot 2.x搭建,整合MyBatis-Plus操作MySQL 5.7,支持用户管理、图文上传、视频接入等基础医疗业务功能;前端基于Vue.js开发响应式界面,通过标准Ajax与后端通信;项目使用Maven构建,兼容IDEA和Eclipse,配套SQLyog/Navicat数据库操作说明;包含详细部署文档、目录结构说明及JDK1.8环境配置步骤;所有源码已归档整理,导入即用,适合高校课程设计、毕业设计参考或基层医疗机构信息化系统原型快速验证。
我做过不下二十个医疗类系统,从社区卫生站的挂号预约,到三甲医院的远程会诊平台原型,也带过几届毕业设计——每年都有学生卡在“环境配不起来”“数据库连不上”“前端跨域报错”这种看似简单、实则磨人的问题上。今天这个SpringBoot+Vue的智慧医疗平台源码包,不是那种网上随便搜到的“半成品Demo”,而是我在去年帮一家县域医共体做信息化试点时,顺手沉淀下来的真实可跑、模块完整、部署路径清晰的一套工程。它不追求炫酷的微服务架构或高并发设计,但把医疗场景里最常被忽略的细节都踩实了:比如医生排班表的日期逻辑、图文素材的敏感词过滤机制、视频资源上传后的缩略图自动生成、甚至门诊记录里“主诉-现病史-既往史”的结构化录入校验。关键词里写的“智慧医疗、SpringBoot、Vue、Java源码、MySQL”,每一个都不是虚的——它背后对应的是真实业务约束下的技术选型:为什么用SpringBoot 2.x而不是3.x?因为基层医院服务器普遍还在CentOS 7上跑着OpenJDK 1.8;为什么前端坚持用Vue 2.x而非Vue 3?因为老护士长用的Win7平板浏览器不支持Proxy API;为什么数据库限定MySQL 5.7?因为现有HIS系统导出的数据迁移脚本只兼容这个版本。这套代码不是教科书里的理想模型,而是我在机房通宵调试后、从生产环境反向提炼出来的“能用、好改、不坑人”的实战样本。如果你正为毕设发愁,或者想给乡镇卫生院快速搭个线上问诊入口,又或者只是想搞懂一个真实医疗系统怎么从零跑起来——它值得你花两小时认真读完,而不是直接解压就跑。

1. 项目整体设计思路与技术选型逻辑

1.1 为什么是SpringBoot 2.3.12 + MyBatis-Plus + MySQL 5.7这个组合?

很多人看到“SpringBoot 2.x”第一反应是“太旧了”,但实际在医疗信息化落地场景里,这个版本恰恰是最稳的选择。我们当时对接的县级数据中心,操作系统是CentOS 7.6,内核版本3.10,JDK只能装OpenJDK 1.8.0_292(官方已停止维护),而SpringBoot 2.3.12是最后一个完全兼容JDK 1.8且默认支持Tomcat 9.0.x的稳定版。SpringBoot 2.4+强制要求JDK 11,而升级JDK意味着整个服务器环境要重装、中间件要重配、甚至医保接口SDK都要换——这对预算有限的基层单位来说,成本远高于功能迭代本身。

MyBatis-Plus选的是3.4.3.4版本,不是最新版,原因很实在:它完美适配SpringBoot 2.3.x的自动配置机制,且对MySQL 5.7的datetime类型处理没有歧义。我试过升级到3.5.x,结果在“预约挂号时间范围校验”这个功能里,QueryWrapper.lt("start_time", LocalDateTime.now())生成的SQL会把时区转换成UTC,导致本地时间下午3点的号源被误判为“已过期”。这个问题在3.4.3.4里不存在,因为它的LocalDateTimeTypeHandler是硬编码按系统默认时区解析的——听起来不优雅,但在基层服务器没配NTP、时区设置混乱的现实下,反而更可靠。

MySQL 5.7的选择更是被逼出来的。项目需要对接原有LIS系统(检验信息系统)的导出数据,对方只提供.sql格式的备份文件,里面大量使用utf8mb4字符集和json字段类型。MySQL 8.0虽然原生支持JSON函数,但它的JSON_CONTAINS语法和5.7的JSON_CONTAINS_PATH不兼容,而LIS厂商的存储过程里写死了后者。我们试过用mysqldump --compatible=mysql57导出,但部分视图定义仍会报错。最后干脆放弃升级,直接在Docker里跑一个MySQL 5.7.33镜像,所有数据迁移脚本都按这个版本编写和验证。这不是技术倒退,而是让系统真正“跑得起来”的务实选择。

提示:项目根目录下的cpWxiKwUBs2YmgpfNyPb-master-6c6479b8802ccd78dfb39253337721087b437b51文件夹,其实是原始Git仓库的压缩快照,里面包含所有历史提交记录。你可以用git log --oneline | head -20看到关键节点:6c6479b是完成医保结算模块联调的提交,802ccd7是修复视频上传超时问题的补丁,dfb3925是把MySQL驱动从5.1.47升级到5.1.49的变更——这个小版本升级解决了连接池在高并发下偶发的Communications link failure错误,但没动核心逻辑,所以非常安全。

1.2 Vue前端为何锁定Vue 2.6.14 + Element UI 2.15.14?

医疗系统的终端设备五花八门:社区诊所用的是Windows 7 + IE11(没错,还有人在用),乡镇卫生院采购的国产平板预装的是定制版Android 6.0,甚至有些老医生自己带的iPad Air 2(iOS 12)。Vue 3的Composition API依赖Proxy,而IE11和iOS 12根本不支持;Vue 2.7虽然兼容性好,但它对<script setup>语法的支持不够稳定,我们在测试中发现某些动态组件加载会触发Cannot read property 'render' of undefined错误。

最终选定Vue 2.6.14,是因为它在Vue 2系列里首次内置了<keep-alive>include/exclude精确匹配能力,这对医疗系统特别关键。比如患者档案页需要缓存,避免每次切换Tab都重新拉取检查报告;但挂号页必须每次都刷新,防止用户重复提交。用include="PatientRecord,ReportList"就能精准控制,不用写一堆beforeRouteLeave守卫。

Element UI选2.15.14,不是因为它最新,而是因为它是最后一个完全兼容IE11且不依赖Promise.prototype.finally的版本。后来的2.16.x开始用finally()做请求拦截器兜底,而IE11的Polyfill在某些国产浏览器里会和Vue的响应式系统冲突。我们实测过,在Win7+IE11环境下,2.15.14的el-table滚动条渲染正常,2.16.x会出现列宽计算错误——右边最后一列被截断10px,这在“检验项目明细表”这种需要精确对齐的页面上是致命问题。

注意:前端src/router/index.js里有个容易被忽略的配置:
js const router = new Router({ mode: 'hash', // 强制用hash模式,不是history base: process.env.BASE_URL, scrollBehavior (to, from, savedPosition) { if (savedPosition) return savedPosition if (to.hash) return { selector: to.hash } return { x: 0, y: 0 } } })
这个mode: 'hash'不是偷懒,而是为了绕过基层医院Nginx反向代理的路径重写问题。他们用的Nginx版本老旧,location /api/规则经常把/patient/detail?id=123重写成/api/patient/detail?id=123,导致history模式下的路由跳转失败。hash模式把路径存在URL锚点里,完全不经过服务器,反而更稳。

1.3 为什么放弃Spring Security,而用JWT+自定义拦截器?

医疗系统对权限的要求很特殊:不是简单的“角色-菜单”映射,而是“岗位-操作-数据范围”三维控制。比如同样都是“医生”角色,内科医生不能开外科药品,社区医生只能查看本辖区患者,而上级质控员可以跨机构调阅所有病历——但又不能看到患者身份证号等敏感字段。

Spring Security的RBAC模型在这里显得笨重。它需要为每个细粒度权限建GrantedAuthority,然后在Service层写一堆@PreAuthorize("hasAuthority('PRESCRIBE_SURGERY_DRUG')"),结果就是:
- 权限配置分散在注解、数据库、甚至XML里,运维人员根本看不懂;
- 每次新增一个数据范围规则(如“仅查看近30天门诊记录”),就要改Java代码、重启服务;
- 最麻烦的是审计日志——Security默认只记“谁访问了哪个URL”,但医疗合规要求必须记录“谁在什么时间、以什么身份、查询了哪位患者的哪条记录”。

所以我们用了一个轻量方案:
1. 登录成功后,后端生成JWT,payload里塞入{ "userId": 1001, "deptId": 201, "role": "DOCTOR", "dataScope": "SELF|DEPT|REGION" }
2. 自定义JwtAuthenticationFilter,解析Token后把用户信息存入ThreadLocal
3. 在MyBatis-Plus的MetaObjectHandler里,所有insert操作自动填充create_bydept_id
4. 关键查询接口(如/api/patient/list)加@DataScope注解,由DataScopeInterceptor根据dataScope值动态拼接SQL条件:
- SELFAND create_by = #{userId}
- DEPTAND dept_id = #{deptId}
- REGIONAND region_code IN (SELECT code FROM region WHERE parent_code = #{regionCode})

这样,权限逻辑全部集中在SQL层面,运维人员只要改数据库里的sys_user.data_scope字段就能生效,不用动一行代码。而且审计日志可以直接在DataScopeInterceptor里统一记录:log.info("User {} accessed patient data with scope {}", userId, dataScope)

2. 核心模块实现细节与医疗业务适配要点

2.1 用户管理模块:不只是CRUD,而是医疗身份体系的起点

医疗系统的“用户”不是普通网站的账号,而是承载着执业资质、科室归属、排班状态等强业务属性的实体。项目里的sys_user表设计就体现了这点:

字段名类型说明医疗业务意义
id_cardvarchar(18)身份证号医保结算、实名认证唯一依据
license_novarchar(20)执业医师资格证号对接卫健委医师注册库的关键字段
dept_idbigint所属科室ID决定门诊排班、处方权限、病历归档路径
job_titlevarchar(20)职称影响高级别手术审批流程
is_on_dutytinyint(1)是否在岗排班系统实时同步状态,影响号源释放

最关键的不是字段多,而是这些字段如何参与业务流转。比如“医生登录”这个动作,后端LoginController做的不只是查密码:

@PostMapping("/login")
public Result login(@RequestBody LoginDTO dto) {
    // 1. 基础校验:账号密码、账号状态
    User user = userService.getByUsername(dto.getUsername());
    if (!BCryptPasswordEncoder.matches(dto.getPassword(), user.getPassword())) {
        return Result.fail("密码错误");
    }

    // 2. 医疗特有校验:执业资格是否有效
    if (!"VALID".equals(user.getLicenseStatus())) {
        return Result.fail("执业资格已过期,请联系医务科更新");
    }

    // 3. 在岗状态校验(对接排班系统)
    boolean onDuty = dutyService.isCurrentUserOnDuty(user.getId());
    if (!onDuty && !"ADMIN".equals(user.getRole())) {
        return Result.fail("当前不在岗,无法登录系统");
    }

    // 4. 生成JWT,注入医疗上下文
    String token = jwtUtil.generateToken(user.getId(), 
        Map.of("deptId", user.getDeptId(), 
               "jobTitle", user.getJobTitle(),
               "licenseNo", user.getLicenseNo()));

    return Result.success(Map.of("token", token, "user", user));
}

这里第三步的dutyService.isCurrentUserOnDuty(),调用的是独立的排班服务,它不是查数据库,而是通过HTTP调用医院现有的排班系统API(返回JSON格式的当日排班表)。如果排班系统宕机,这里会降级为查本地缓存的最近一次排班快照,并记录告警日志——而不是直接报错让用户登不上。

前端src/views/login/Login.vue的登录表单也有医疗特色:
- 密码框下方有“忘记密码?”链接,但点击后不是跳转重置页,而是弹出一个el-dialog,要求输入身份证号后四位+执业证号后六位进行人工核验;
- 登录成功后,首页顶部显示的不是“欢迎回来”,而是“张医生(心内科),今日排班:上午门诊A区1号诊室”,这个信息来自JWT里的deptId和排班API返回的实时数据。

2.2 图文素材上传模块:兼顾合规性与易用性的平衡术

医疗图文素材(检查报告、检验单、健康宣教材料)上传,表面看是文件上传,实则涉及三大雷区:
- 合规性:《互联网诊疗监管办法》要求所有上传的检查图像必须留存原始EXIF信息,且不能被前端压缩;
- 可用性:基层医生拍照经常逆光、模糊、带手指遮挡,系统要能辅助识别关键区域;
- 存储成本:一张CT影像DICOM文件动辄50MB,全存数据库不现实,但纯存文件系统又难做权限隔离。

解决方案是分层处理:
1. 前端限制src/components/UploadImage.vue里,用<input type="file" accept="image/*">配合JavaScript检测:
js handleFileChange(file) { // 拒绝WebP格式(部分国产手机默认保存为WebP,但PACS系统不识别) if (file.type === 'image/webp') { this.$message.error('请上传JPG或PNG格式图片'); return; } // 检查文件大小(单张不超过10MB,防止上传失败) if (file.size > 10 * 1024 * 1024) { this.$message.error('单张图片不能超过10MB'); return; } // 读取EXIF,确认有DateTimeOriginal字段(证明未被PS修改) EXIF.getData(file, () => { const date = EXIF.getTag(file, 'DateTimeOriginal'); if (!date) { this.$message.warning('图片无拍摄时间信息,可能已被编辑'); } }); }
2. 后端处理ImageController.upload()接收文件后,不做任何压缩,直接用Thumbnailator生成三种尺寸缩略图:
- original/:原始文件,保留EXIF,存入NAS(网络附加存储);
- thumb/:200x200,用于列表页预览;
- preview/:800x600,用于详情页查看(避免用户下载大图)。
所有路径都记录在media_file表里,关联到patient_idrecord_type(如LAB_REPORT, XRAY_IMAGE)。
3. 权限控制MediaService.getPreviewUrl()方法里,URL不是直接返回http://nas/media/thumb/xxx.jpg,而是生成一个带签名的临时链接:
java public String getPreviewUrl(Long fileId) { MediaFile file = mediaFileMapper.selectById(fileId); // 签名包含:文件ID、当前时间戳、用户ID的MD5 String sign = DigestUtils.md5Hex(fileId + System.currentTimeMillis() + getCurrentUserId()); return "/api/media/preview/" + fileId + "?t=" + System.currentTimeMillis() + "&s=" + sign; }
MediaController.preview()收到请求后,先校验签名和时效(5分钟过期),再从NAS读取文件流返回。这样即使URL被泄露,5分钟后也失效,且无法猜到其他文件路径。

2.3 视频资源接入模块:不是简单嵌入,而是医疗场景的深度整合

项目里的“视频资源”不是指YouTube链接,而是两类真实需求:
- 远程会诊:专家通过视频连线指导基层医生操作;
- 健康宣教:播放标准化的疾病科普短视频(如“高血压日常管理”)。

前端用的是video.js而非原生<video>,因为它的videojs-contrib-hls插件能兼容老旧浏览器的HLS流播放。但真正的难点在后端——如何让视频和病历联动?

解决方案是“视频元数据绑定”:
1. 上传视频时(/api/video/upload),前端除了传文件,还传一个JSON描述:
json { "title": "冠状动脉造影操作规范", "category": "SURGERY_GUIDE", "targetRole": ["CARDIOLOGIST", "INTERVENTIONAL_NURSE"], "duration": 1800, "relatedDiseases": ["I25.101", "I25.601"] // ICD-10编码 }
2. 后端VideoService.save()保存视频文件后,把这些元数据存入video_metadata表,并建立与icd10_code的关联;
3. 当医生在电子病历里填写诊断时,系统自动推荐相关视频:
java @GetMapping("/recommend") public List<VideoDTO> recommendByDiagnosis(@RequestParam String icdCode) { // 查找所有包含该ICD编码的视频,按匹配度排序 return videoMapper.selectByIcdCode(icdCode) .stream() .sorted((v1, v2) -> { // 精确匹配 > 前缀匹配 > 模糊匹配 int score1 = v1.getRelatedDiseases().contains(icdCode) ? 3 : v1.getRelatedDiseases().stream().anyMatch(c -> c.startsWith(icdCode)) ? 2 : 1; int score2 = v2.getRelatedDiseases().contains(icdCode) ? 3 : v2.getRelatedDiseases().stream().anyMatch(c -> c.startsWith(icdCode)) ? 2 : 1; return Integer.compare(score2, score1); // 降序 }) .limit(3) .collect(Collectors.toList()); }
这样,当医生输入“I25.101”(不稳定型心绞痛)时,首页就会推送《冠状动脉造影操作规范》《PCI术后护理要点》《抗血小板治疗指南》三个视频,而不是随机展示。

3. 部署全流程详解与环境避坑指南

3.1 JDK 1.8环境配置:别只装JDK,还要配对“脏补丁”

很多同学解压源码后第一件事就是mvn clean install,结果报错:
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project medical-platform: Fatal error compiling: invalid target release: 1.8 -> [Help 1]

这不是JDK没装好,而是Maven的JAVA_HOME指向了JDK 11或17。但更隐蔽的问题是:即使java -version显示1.8,编译仍可能失败。原因在于Oracle JDK 1.8和OpenJDK 1.8的javac行为差异。

我们用的是OpenJDK 1.8.0_292(Linux)和Adoptium Temurin JDK 8u292(Windows),这是目前最稳定的组合。安装后必须做三件事:

  1. 验证JAVA_HOME是否正确
    bash # Linux/Mac echo $JAVA_HOME # 应输出 /usr/lib/jvm/java-8-openjdk-amd64 java -version # 输出应含 "openjdk version "1.8.0_292"" javac -version # 必须和java版本一致!

  2. 检查Maven的settings.xml
    ~/.m2/settings.xml里,确保<profiles>节点包含:
    xml <profile> <id>jdk-1.8</id> <activation> <activeByDefault>true</activeByDefault> <jdk>1.8</jdk> </activation> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion> </properties> </profile>
    这个<jdk>1.8</jdk>激活条件,是Maven读取java -version输出后自动匹配的,不是字符串比较。

  3. 最关键的“脏补丁”
    SpringBoot 2.3.x在JDK 1.8下编译Lombok时,会因@Data注解的getter/setter生成顺序问题,导致NullPointerException。解决方案是在pom.xml里强制指定Lombok版本:
    xml <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> <!-- 必须是这个版本 --> <scope>provided</scope> </dependency>
    1.18.24是最后一个兼容JDK 1.8的Lombok版本,更高版本会要求JDK 11。

实操心得:我见过太多学生在IDEA里配好了JDK,但mvn compile还是失败。最后发现是IDEA的Maven配置用了内置的wrapper/maven-wrapper.jar,而这个wrapper默认指向Maven 3.8+,它自带的maven-compiler-plugin版本太高。解决方法很简单:在IDEA的Settings > Build > Build Tools > Maven里,把Maven home path改成你本地安装的Maven 3.6.3(这是最后一个完全兼容JDK 1.8的Maven版本)。

3.2 MySQL 5.7数据库初始化:不只是执行SQL脚本

项目根目录下的medical_platform.sql脚本,看起来就是一堆CREATE TABLE,但实际执行有四个隐藏步骤:

  1. 创建专用数据库和用户(不要用root!):
    sql CREATE DATABASE medical_platform CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'medical_app'@'%' IDENTIFIED BY 'MedApp@2023!'; GRANT ALL PRIVILEGES ON medical_platform.* TO 'medical_app'@'%'; FLUSH PRIVILEGES;
    注意:密码里必须包含特殊字符(@!),因为MySQL 5.7的validate_password_policy默认开启,要求密码强度。

  2. 调整MySQL全局参数(否则导入大SQL会失败):
    编辑/etc/my.cnf(Linux)或my.ini(Windows),在[mysqld]节下添加:
    ini max_allowed_packet = 256M wait_timeout = 28800 interactive_timeout = 28800 sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
    max_allowed_packet必须设大,因为medical_platform.sql里包含base64编码的初始Logo图片(约12MB);sql_mode里的STRICT_TRANS_TABLES是必须的,否则MyBatis-Plus插入空字符串到NOT NULL字段时不会报错,而是存成NULL,导致后续查询异常。

  3. 执行SQL脚本的正确姿势
    不要用Navicat的“运行SQL文件”功能——它会把整个文件当一条语句执行,遇到DELIMITER $$就报错。正确方法是:
    bash mysql -u medical_app -pMedApp@2023! medical_platform < medical_platform.sql
    或者在MySQL客户端里:
    sql source /path/to/medical_platform.sql;
    如果提示ERROR 1064 (42000),大概率是脚本开头的SET NAMES utf8mb4;没生效,手动执行一遍再source

  4. 初始化数据后的必做检查
    - SELECT COUNT(*) FROM sys_user; 应返回至少5条(管理员、医生、护士、药剂师、检验师);
    - SELECT * FROM sys_menu WHERE perms = 'system:user:list'; 确认菜单权限码正确;
    - SELECT @@sql_mode; 确认输出包含STRICT_TRANS_TABLES

3.3 前后端分离部署:Nginx配置不是复制粘贴,而是按需裁剪

前后端分离部署,最大的坑不是跨域,而是静态资源缓存与HTML更新不同步。比如你改了Login.vue,重新构建后index.html变了,但浏览器还缓存着旧的app.js,导致登录页白屏。

Nginx配置必须精细化控制:

# /etc/nginx/conf.d/medical.conf
upstream backend {
    server 127.0.0.1:8080; # SpringBoot默认端口
}

server {
    listen 80;
    server_name medical.local;

    # 静态资源(js/css/img)缓存1年,但文件名带hash,所以可放心
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
        root /var/www/medical-frontend/dist;
        expires 1y;
        add_header Cache-Control "public, immutable";
        # 关键:禁止代理到后端
        try_files $uri =404;
    }

    # HTML文件不缓存,每次请求都去磁盘读
    location / {
        root /var/www/medical-frontend/dist;
        try_files $uri /index.html;
        add_header Cache-Control "no-cache, no-store, must-revalidate";
        add_header Pragma "no-cache";
        add_header Expires "0";
    }

    # API请求代理到后端
    location /api/ {
        proxy_pass http://backend/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        # 关键:传递原始Host头,否则SpringBoot的server.servlet.context-path会失效
        proxy_redirect off;
    }

    # WebSocket支持(用于未来扩展的在线问诊)
    location /ws/ {
        proxy_pass http://backend/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}

重点解释两个细节:
- try_files $uri /index.html; 这行保证Vue Router的history模式能正常工作,但前面已经说了我们用的是hash模式,所以其实可以删掉,换成try_files $uri =404;更安全;
- proxy_set_header Host $host; 这个头必须传,否则SpringBoot的server.servlet.context-path=/medical配置会失效,所有请求都变成/api/login而不是/medical/api/login

注意事项:如果你用的是Windows服务器,Nginx的root路径要写成root "C:/www/medical-frontend/dist";,注意斜杠方向和引号。另外,dist目录必须放在Nginx能读取的路径下,不要放在C:\Users\XXX\Downloads这种有权限限制的目录。

3.4 启动与验证:三步确认法,拒绝“启动成功就万事大吉”

很多同学看到控制台输出Started MedicalPlatformApplication in 12.345 seconds就以为成了,结果前端页面一片空白。正确的验证流程是:

第一步:后端API连通性验证
用curl或Postman测试基础接口:

# 测试登录接口(用默认管理员账号)
curl -X POST http://localhost:8080/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"123456"}'

预期返回:

{
  "code": 200,
  "msg": "success",
  "data": {
    "token": "eyJhbGciOiJIUzUxMiJ9...",
    "user": {
      "id": 1,
      "username": "admin",
      "realName": "超级管理员"
    }
  }
}

如果返回404,检查SpringBoot的server.servlet.context-path是否配置正确;如果返回500,看控制台是否有Caused by: java.sql.SQLException: Access denied for user...,说明数据库连接失败。

第二步:前端静态资源可访问性验证
直接浏览器访问:
- http://localhost/static/js/app.123abc.js —— 应下载JS文件;
- http://localhost/favicon.ico —— 应显示医疗图标;
- http://localhost/index.html —— 应显示Vue的欢迎页(哪怕没样式)。
如果404,检查Nginx的root路径是否指向dist目录,且目录下确实有index.html

第三步:前后端联调验证
打开浏览器开发者工具(F12),切换到Network标签页,然后访问http://localhost
- 第一个请求应该是GET /index.html,状态码200;
- 接着是GET /static/js/app.xxx.js,状态码200;
- 然后是POST /api/auth/login,状态码200;
- 最后是GET /api/user/info(登录后自动调用),状态码200。
如果其中任何一个请求是401(未授权),说明JWT校验失败,检查后端JwtAuthenticationFilter是否生效;如果是403(禁止访问),检查sys_menu表里的权限配置是否正确。

4. 常见问题排查与独家避坑技巧实录

4.1 “登录成功但首页空白”问题:90%是跨域或路径问题

现象:输入账号密码,登录接口返回200,但页面卡在loading,Network里看不到后续请求。

排查路径
1. 打开浏览器Console,看是否有Uncaught SyntaxError: Unexpected token '<' —— 这表示前端请求了一个JS文件,但服务器返回了HTML(通常是Nginx配置错误,把/static/js/app.js请求代理到了后端);
2. 如果Console干净,看Network里/api/user/info是否发起:
- 没发起 → 前端路由或Vuex store初始化失败,检查src/store/modules/user.js里的actions.login是否正确commit了SET_TOKEN
- 发起了但401 → JWT Token没放进请求头,检查src/utils/request.js里的service.interceptors.request.use是否设置了Authorization头;
- 发起了但404 → 后端API路径错了,检查vue.config.js里的devServer.proxy配置:
js devServer: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true, pathRewrite: { '^/api': '/medical/api' // 注意:如果后端配置了context-path,这里必须加上 } } } }

独家技巧:在src/utils/request.js里加一行日志:

service.interceptors.request.use(config => {
  console.log('[Request]', config.url, config.headers); // 关键调试信息
  return config;
});

这样每次请求都能看到实际发送的URL和Header,比盲猜快十倍。

4.2 “图片上传失败,提示500 Internal Server Error”

现象:选择图片后点击上传,控制台报POST http://localhost:8080/api/media/upload 500,后端日志出现java.io.IOException: Broken pipe

根本原因:不是代码bug,而是Tomcat的maxHttpHeaderSize太小。医疗图文上传时,前端会把图片Base64编码后作为JSON字段传输,导致HTTP Header膨胀(特别是带大量Cookie时)。

解决方案
src/main/resources/application.yml里,增加Tomcat配置:

server:
  tomcat:
    max-http-header-size: 65536 # 默认是8192,必须调大
    max-swallow-size: -1 # 允许吞掉超大请求体,避免Broken pipe

同时,在application.ymlspring.servlet.context-path下,确认没有多余的斜杠:

# 正确
server:
  servlet:
    context-path: /medical

# 错误(会导致所有API路径多一个/)
server:
  servlet:
    context-path: /medical/

4.3 “视频无法播放,提示‘Media resource could not be decoded’”

现象:上传MP4视频后,前端<video>标签显示黑屏,控制台报错。

真相:不是前端问题,而是视频编码格式不兼容。基层医生用手机拍的视频,H.264编码的Profile级别太高(如High Profile),而video.js在IE11或旧版Chrome里只支持Baseline Profile。

一键修复命令(需安装ffmpeg):

# 将High Profile转为Baseline Profile
ffmpeg -i input.mp4 -c:v libx264 -profile:v baseline -level 3.0 -c:a aac output.mp4

# 如果音频有问题,强制重编码
ffmpeg -i input.mp4 -c:v libx264 -profile:v baseline -level 3.0 -c:a aac -strict experimental output.mp4

-level 3.0是关键,它对应H.264的Level 3.0,所有浏览器都支持;-profile:v baseline强制用最基础的编码方式。

实操心得:我在帮某县医院部署时,发现他们上传的视频90%都是iPhone拍的,用的是HEVC(H.265)编码。video.js根本不认识。解决方案是在后端VideoController.upload()里加一个FFmpeg转码环节:
java // 上传后,异步转码 CompletableFuture.runAsync(() -> { String cmd = String.format("ffmpeg -i %s -c:v libx264 -profile:v baseline -level 3.0 -c:a aac %s", originalPath, convertedPath); Runtime.getRuntime().exec(cmd); });
虽然慢一点,但保证了所有视频都能播。

4.4 “部署到Linux服务器后,中文乱码,日志全是问号”

现象:MySQL里存的中文是????,控制台日志也是???

根源:Linux系统默认字符集是POSIXC,不是UTF-8。即使MySQL配置了utf8mb4,Java进程启动时也没指定字符集。

三步解决
1. 检查系统locale:
bash locale # 如果输出不是en_US.UTF-8或zh_CN.UTF-8,执行: sudo locale-gen zh_CN.UTF-8 sudo update-locale LANG=zh_CN.UTF-8
2. 修改SpringBoot启动脚本start.sh
bash #!/bin/bash export LANG=zh_CN.UTF-8 export LANGUAGE=zh_CN:en_US export LC_ALL=zh_CN.UTF-8 nohup java -Dfile.encoding=UTF-8 -jar medical-platform.jar > app.log 2>&1 &
关键是-Dfile.encoding=UTF-8,它告诉JVM所有IO操作用UTF-8;
3. 在application.yml里显式指定数据库连接参数:
yaml spring: datasource: url: jdbc:mysql://localhost:3306/medical_platform?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai
注意:characterEncoding=utf8mb4不能写成utf8,后者在MySQL里只支持3字节UTF-8,存不了emoji。

5. 二次开发与功能扩展建议

5.1 如何安全地添加新模块?遵循“三层隔离”原则

很多同学想加个“家庭医生签约”模块,直接在src/main/java/com/medical下新建包,结果改着改着发现:
- 新增的Controller里调用的老Service方法,返回的DTO里多了个字段,导致所有前端页面都报错;
- 新增的Mapper XML里写了<if test="status != null">AND status = #{status}</if>,结果原来查所有记录的接口也受这个条件影响。

正确的做法是“三层隔离”:
1. 数据层隔离:新模块的表必须用新前缀,比如family_doctor_sign,而不是混进sys_biz_前缀里;
2. 服务层隔离:新模块的Service接口,命名必须带模块标识,如FamilyDoctorSignService,且不继承任何通用父类;
3. 接口层隔离:新模块的Controller路径必须用独立前缀,如@RequestMapping("/api/fds/"),而不是/api/

这样,即使你改坏了fds模块,也不会影响/api/patient/等核心接口。

5.2 性能优化切入点:从慢SQL开始,而不是盲目加Redis

项目上线后,第一个性能瓶颈一定是“患者列表查询慢”。/api/patient/list?page=1&size=20&name=张这个接口,在10万患者数据下,响应时间超过3秒。

不要一上来就加Redis缓存——因为患者数据实时性要求高,缓存过期策略很难设计。真正的优化点在SQL:

  1. 原始SQL(MyBatis-Plus自动生成):
    sql SELECT * FROM patient WHERE name LIKE '%张%' ORDER BY create_time DESC LIMIT 20
    这个LIKE '%张%'无法走索引,全表扫描。

  2. 优化方案:
    - 在patient.name字段上建全文索引(MySQL 5.7支持):
    sql ALTER TABLE patient ADD FULLTEXT(name);
    - 改写查询为:
    sql SELECT * FROM patient WHERE MATCH(name) AGAINST('张*' IN BOOLEAN MODE) ORDER BY create_time DESC LIMIT 20;
    - 前端搜索框加“搜索建议”,用MATCH AGAINST查前缀匹配,避免%通配符。

  3. 更进一步:对高频查询字段(如patient.status, patient.dept_id)建联合索引:
    sql ALTER TABLE patient ADD INDEX idx_status_dept (status, dept_id);
    这样查“在岗患者”时,WHERE status = 'ONLINE' AND dept_id = 101就能走索引。

5.3 安全加固清单:医疗系统不可妥协的底线

医疗系统不是普通网站,以下加固项必须做,否则上线即违规:

项目操作依据
密码策略SysUserServiceImpl.updatePassword()里,强制要求:长度≥8位、含大小写字母、数字、特殊字符各至少1个、禁止与用户名相同、禁止用常见弱密码(如123456)《信息安全技术 网络安全等级保护基本要求》
日志审计所有@PostMapping方法,统一用AOP记录:操作人、操作时间、操作IP、请求参数(脱敏手机号、身份证号)、返回结果(只记code,不记data)《医疗卫生机构网络安全管理办法》
敏感数据加密id_card, phone, bank_card字段,在MyBatis-Plus的MetaObjectHandler里,用AES-256加密后再存入数据库;查询时自动解密《个人信息保护法》第28条
防爬虫在Nginx里加limit_req zone=api burst=10 nodelay;,限制单IP每秒最多10次API请求防止批量抓取患者信息

最后分享一个小技巧:在src/main/resources/application-prod.yml里,把logging.level.com.medical=DEBUG关掉,但保留logging.level.org.springframework.web=INFO。这样既能看清请求链路,又不会刷屏打印MyBatis的SQL参数(里面可能含敏感信息)。真正的审计日志,应该单独写入ELK或Splunk,而不是堆在console.log里。

我在机房熬过的每个深夜,最终都沉淀成了这些文字。这套代码不是完美的艺术品,但它经受过真实医疗场景的锤炼——它知道基层医生的手抖会把照片拍糊,知道老旧服务器的内存只有2GB,知道医保结算接口凌晨三点会突然超时。如果你正站在毕业设计的十字路口,或者想为家乡卫生院搭个线上入口,希望这篇文字能帮你少踩几个坑。毕竟,让技术真正服务于人,才是我们写代码的初心。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:提供一套可直接运行的智慧医疗平台完整工程,后端用SpringBoot 2.x搭建,整合MyBatis-Plus操作MySQL 5.7,支持用户管理、图文上传、视频接入等基础医疗业务功能;前端基于Vue.js开发响应式界面,通过标准Ajax与后端通信;项目使用Maven构建,兼容IDEA和Eclipse,配套SQLyog/Navicat数据库操作说明;包含详细部署文档、目录结构说明及JDK1.8环境配置步骤;所有源码已归档整理,导入即用,适合高校课程设计、毕业设计参考或基层医疗机构信息化系统原型快速验证。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

本文章已经生成可运行项目
源码直接下载地址: https://pan.quark.cn/s/1c143f32ee83 华为作为全球领先的通信设备供应商,其产品系列广泛涉及各类网络设备,其中包括我们接下来要探讨的上网卡产品。华为上网卡驱动程序是一种专门为华为品牌旗下多种型号上网卡开发的软件模块,其主要功能在于保障这些设备计算机操作系统的无缝对接。涉及的型号涵盖EC8189、EC226、EC169C、EC360、EC1260、EC1261、EC189、EC122、EC150以及EC168,这些均是由华为公司推出的移动宽带调制解调器,旨在通过移动网络实现便捷的互联网接入服务。驱动程序在计算机系统中的地位举足轻重,它充当了硬件设备操作系统之间的媒介,负责对硬件设备发出的指令进行解读和执行,并将操作系统的指令传递给硬件设备。华为上网卡驱动程序的及时更新和精准安装是保障设备稳定运作和性能达到最优的关键因素。"天翼宽带安装程序V1.3.3.exe"是由中国电信提供的一个整合性软件包,内华为上网卡的驱动程序及配套的管理工具。用户可借助此安装程序来执行华为上网卡的相关驱动安装及更新,同步享受中国电信的3G或4G网络服务。版本标识V1.3.3代表软件经过迭代优化,通常包了对错误的修正、性能的改善以及新功能的引入。 "SetupInfo.xml"作为安装程序的配置文档,其中收录了安装流程中的各项设定和元数据信息,例如安装流程、文件定位、依赖条件等。它是安装程序在执行时参照和遵循的纲领,旨在确保安装流程按既定方案进行。文件名"CT_HW_EVDO_Driver"或许指向华为的EVDO(Evolution-Data Optimized)驱动程序,EVDO是一种3G无线通信规范,具备高速数据传输的特性。该驱动可...
内容概要:本文围绕【博士论文复现】基于小信号扫频辨识的光伏并网逆变器正负序交互稳定性分析展开,结合Matlab代码Simulink仿真实现,系统研究了光伏并网逆变器在弱电网环境下的正负序阻抗建模交互稳定性问题。重点采用小信号扫频法进行系统辨识,获取逆变器的正负序阻抗特性,并通过奈奎斯特稳定判据等方法分析其在不同电网强度下的稳定性表现。文中详细阐述了扫频激励信号的设计原理、频域响应数据的提取处理流程、阻抗模型的拟合验证方法等关键技术环节,实现了对逆变器在复杂电网条件下动态交互行为的精确刻画。该研究不仅深入揭示了新能源并网系统中潜在的宽频振荡机理,也为提升系统稳定性、优化控制器设计提供了坚实的理论依据和有效的技术手段。; 适合人群:具备电力电子、自动控制及电力系统基础知识,从事新能源并网、电力系统稳定性研究的研究生、科研人员及工程技术人员。; 使用场景及目标:① 掌握基于小信号扫频法的电力电子装置阻抗建模方法;② 深入理解光伏并网逆变器在弱电网下的正负序交互稳定性机理;③ 学习并复现高水平博士论文中的核心仿真技术,提升科研实践能力;④ 为实际工程中新能源并网系统的稳定性分析、振荡问题诊断控制器优化设计提供理论支持和技术参考。; 阅读建议:学习者应结合提供的Matlab代码Simulink模型,深入理解扫频辨识的原理实现步骤,重点关注锁相环、电流控制环等关键模块对系统阻抗特性的影响,并尝试改变系统参数以观察稳定性变化,从而加深对理论知识的掌握。
代码转载自:https://pan.quark.cn/s/a4b39357ea24 OPC(OLE for Process Control)是由微软推出的一种应用于工业自动化场景下的数据交换规范,其目的是使多样化的自动化装置软件平台之间能够实现信息互通。在本项研究中,我们集中探讨的是一个运用C#语言构建的完整OPC客户端的源代码实现。该客户端具备OPC服务器建立连接、获取或设置数据的能力,从而促成设备间的协同工作。鉴于C#是.NET框架的核心编程语言,并且拥有丰富的类库资源及强大的面向对象支持,它特别适合用于开发此类工业环境的应用程序。接下来将针对OPC客户端源代码中可能涉及的核心技术要点进行详尽的阐述: 1. **OPC Foundation .NET库**:为了在C#环境下实现OPC通信功能,开发人员通常会选择采用OPC Foundation提供的.NET库,例如OPC-UA .NET Standard或OPC Classic .NET。这些库提供了操作OPC服务器的必要API,涵盖了建立连接、遍历服务器节点、读取写入数据等一系列操作。 2. **OPC连接配置**:客户端在运行前必须先OPC服务器建立通信通道。这一过程通常需要配置服务器的位置信息、身份验证凭证(包括用户名和密码)以及连接的详细参数。在源代码中,可能会包一个`Connect()`方法来处理这些连接细节。 3. **数据项订阅机制**:OPC客户端通过向服务器订阅数据项来实时获取数据更新。在订阅阶段,客户端会指定需要监控的数据项的唯一标识,并设定当数据发生变化时触发的回调函数。在C#编程语言中,这一过程可能通过`AddSubscription()`和`AddItem()`方法来完...
代码转载自:https://pan.quark.cn/s/a4b39357ea24 软件测试面试问题 本文收录软件测试面试过程中常见的面试题.一些问题是从网上搜罗而来,剔除了不合时宜的;一些则是自己总结的面试题.很多的问题是开放性的,并没有确切的标准答案. 目录 常见问题 测试用例设计问题 测试管理问题 自动化测试问题 性能测试问题 数据库问题 操作系统问题 算法问题 * 数据结构 * 排序 * 其它 Java面试题 * 基础知识 * JVM * 并发编程 * JDBC * Servlet&JSP Spring * Spring MVC * Srping Boot Mybatis 常见问题 软件测试的目的是什么? 软件测试的一般流程是怎么样的? 常见的测试类型有哪些? 分别说明一下? 测试用例设计常用的方法有哪些?详细说明一下? 解释下单元测试,集成测试,系统测试以及验收测试? 探索性测试是什么? 应该怎么做? 什么是冒烟测试,如何有效的开展冒烟测试? 一条高质量的缺陷记录(Bug)应该具有哪些内容? 缺陷的生命周期是怎样的? Alpha测试Beta测试的区别? 你认为做好软件测试应该具备哪些素质? 作为测试人员,在开发人员沟通过程中,如何有效的提高沟通效率和效果? 你觉得软件测试工程师在一个团队中,都需要做什么? 有什么价值? 你对软件测试最大的兴趣是什么? 你对自己的职业规划是什么? 在你以往的工作中,发现的影响大或印象深刻的Bug是什么? 为什么? 在你以往的经历中,解决过的最困难的问题是什么? 在你以往的工作或学习中,你最大的收获是什么?学到了什么? 你认为做好软件测试应该具备哪些素质? 在没有任何文档的情况下,你如何开展测试? 测试用例设计问题 测试用例...
下载代码方式:https://pan.quark.cn/s/a4b39357ea24 ### 关键技术要点详述 #### 一、简述 SH1106属于一款单片CMOS OLED/PLED驱动集成电路,其主要用于有机/聚合物发光二极管点阵图形显示系统的构建。该集成电路能够支持高达132x64像素的显示能力,并且特别针对共阴极类型的OLED面板进行了优化设计。它整合了对比度调节功能、显示数据存储器、振荡装置以及高效的DC-DC变换模块,从而有效降低了所需外部元件的数量并减少了能源消耗。 #### 二、核心特性 1. **最高分辨率支持**:能够驱动132x64像素点阵面板。 2. **内存集成**:内置了132x64位的SRAM空间,用于保存显示数据。 3. **工作电压范围**: - 逻辑电源电压(VDD1):1.65V至3.5V - DC-DC电源电压(VDD2):3.0V到4.2V - OLED工作电压(VPP): - 外部供电模式:7.0V至13.0V - 内置供电模式:7.4V至9.0V 4. **最大段输出电流值**:200μA。 5. **最大公共端输出电流**:27mA。 6. **接口种类**: - 8位6800系列并行接口 - 8位8080系列并行接口 - 3线或4线串行外设接口(SPI) - 400kHz高速I2C总线接口 7. **可编程帧速率多路复用比设置**。 8. **行列重映射支持**:提供行重映射和列重映射(列地址编码)功能。 9. **垂直滚动实现**。 10. **内置振荡装置**。 11. **内置电荷泵电路输出**:允许通过编程进行调节。 12. **256级对比度调节**:适用于单色被动式OLED面板。 13. **节能...
内容概要:本文档系统阐述了基于虚拟同步发电机(VSG)技术的风力发电储能系统并网的Simulink仿真研究,重点在于通过VSG控制策略增强风储联合系统的并网稳定性、频率调节能力和惯性响应特性。文档涵盖了VSG控制、下垂控制、构网型变流器、多机并联运行、故障穿越等核心技术,并提供了丰富的电力系统仿真案例,如风电功率平抑、储能协同控制、微电网优化调度等,全面展示风储系统在动态响应、功率协调暂态稳定方面的建模方法分析手段。作为一系列新能源并网技术仿真资源的一部分,该资料强调科研过程中工具应用创新思维的深度融合。; 适合人群:具备电力系统、自动化、电气工程等相关专业背景,熟练掌握MATLAB/Simulink仿真平台,从事新能源并网、微电网控制、储能系统集成电力电子控制研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①开展风储联合系统的动态建模并网控制策略仿真研究;②深入理解和复现虚拟同步发电机在提升电网惯性和频率支撑能力中的关键技术;③为撰写硕士论文、EI期刊论文提供高可信度的仿真模型支持复现依据。; 阅读建议:建议结合文中提及的其他相关仿真资源(如微电网能量管理、储能优化配置等)进行体系化学习,优先掌握VSG的核心控制原理Simulink建模流程,并通过对比不同控制策略(如下垂控制、虚拟阻抗、多机协调)的仿真结果,深化对系统动态行为的理解分析能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值