首页 / 资讯中心 / 文章详情

Vue3+SpringBoot接入DeepSeek:症状自查与结构化电子病历生成实战

Vue3+SpringBoot接入DeepSeek:症状自查与结构化电子病历生成实战 ★ FEATURED ARTICLE
简介这是一套基于 Vue 与 SpringBoot 构建的智慧医院就诊系统完整毕业设计资源包面向医疗信息化方向的高校学生、Java 全栈开发者及医院信息系统技术人员。系统覆盖预约挂号、智能问诊、医生工作台、科室排班、患者服务、系统日志与权限管理等核心模块并将 DeepSeek 大语言模型融入诊疗流程创新实现症状自查、结构化电子病历生成与临床建议辅助有助于减少医生非临床工作负担。资源共 710 个文件以 Java 源码、Vue 组件、JavaScript 脚本、CSS 样式、SQL 数据库脚本及说明文档为主另含图片和动态演示素材压缩包约 10.08MB目录结构清晰便于按模块阅读和二次开发。当前已有 243 人学习适合用于理解医院就诊业务闭环也可作为毕业设计答辩演示、功能扩展或代码重构的参考基础。1. 把 VueSpringBoot 就诊系统讲透DeepSeek 症状自查与电子病历生成的落地方案做智慧医院就诊系统最难的不是挂号、排队叫号那些常规模块而是“怎么让系统看起来真的懂医疗”。大部分毕业设计开源项目止步于 CRUD 增删改查界面再华丽业务逻辑还是“查表 状态机”。这套基于 Vue 3 SpringBoot 3 的就诊系统源码真正不一样的地方在于接入了 DeepSeek 大语言模型把症状自查、结构化电子病历生成、临床建议辅助这三件事做成了可运行的功能而不是 PPT 上的概念。我拆完整个前后端代码和数据库脚本后确认它适合两类人一类是正在做毕设但不想只交“管理系统”的学生另一类是准备把 LLM 塞进传统业务系统、想少走弯路的 Java 工程师。你能直接看到模型接口怎么封装、业务层怎么兜底、前端流式输出怎么接这比从头读文档高效太多。整套系统覆盖了患者端、医生端和管理员端三个角色核心链路是“患者描述症状 → DeepSeek 返回候选疾病和问诊建议 → 医生选择生成结构化电子病历 → 系统给出临床建议”。源码里带有完整数据库建表脚本MySQL包含用户表、预约表、病历表、药品表、科室表等视图和存储过程也有几个能直接跑通预约挂号、门诊就诊、病历归档的闭环。我前后花了一个多星期把每个模块从“能跑”调试到“能讲”下面这篇笔记按资源是什么、结构怎么拆、DeepSeek 怎么接、病历和症状自查怎么落地、坑在哪、怎么把系统做亮这几个顺序把这份资源彻底讲清楚。2. 系统架构与代码包结构先看清前后端边界和 LLM 的介入位置2.1 三层架构里的模型调用设计为什么是 Vue3 SpringBoot 而不是单体模板引擎传统就诊系统常做在 JSP Servlet 或 Thymeleaf 模板里页面跳来跳去前后端耦合严重。这套源码采用前后端分离SpringBoot 提供 RESTful APIVue 3 通过 axios 发起请求。这样设计不是图“时髦”而是因为接入 DeepSeek 后前端需要处理流式输出和异步状态模板引擎在这种交互下会非常别扭。后端模块我拆开看主要分这几块authentication基于 JWT 的登录鉴权医生和患者走不同角色拦截器business挂号预约、分诊、收费、药房等传统模块aiDeepSeek 调用封装包括 prompt 构建、响应解析、异常兜底medical病历管理、诊断建议、检验报告的结构化存储单看模块划分不稀奇关键在于 ai 模块的“隔离性”。项目把大模型调用单独放在一个 service 里前端所有 AI 功能都经由/api/ai/*统一入口进入不进业务表直接写数据库。这样做有实际意义模型输出是不可控的万一某次返回了非 JSON 内容你不会把脏数据污染到病历表里前端也可以根据接口状态单独展示“AI 生成中”的 Loading而不会拖慢挂号接口。前端项目结构里重点看src/api和src/views/ai两个目录。前者是接口统一封装层后者是症状自查和病历生成的前端交互页面。Vue 3 用组合式 API 写逻辑状态管理只用了一个极简的 pinia store 来存用户信息和 JWT没有过度设计。提示资源里自带的 README 有早期版本说明里面“引入 DeepSeek”这几个字实际实现比说明文档多了不少细节建议以源码实际行为为准。2.2 数据库表设计病历表、用户表、症状表如何支撑“结构化”需求结构化电子病历的“结构化”不是指前端把文本塞进textarea就叫结构化而是数据库里要有明确的字段承接不同粒度的医疗数据。打开sql目录里的建表脚本核心表有这么几张user患者和医生共用一张表用role字段区分存姓名、手机号、加密后的登录密码department科室表关联医生排班appointment预约挂号表包含时间槽和就诊状态medical_record病历主表涵盖主诉、现病史、既往史、初步诊断、处理建议和开药信息symptom_check症状自查记录表存用户输入的自然语言症状文本和模型返回的结构化结果JSON 字段ai_logAI 调用日志表记每次请求的输入 token、输出 token 和耗时这里的symptom_check和ai_log两张表是这套资源区别于普通管理系统的地方。symptom_check表里有一个result_json字段类型用了TEXT专门承接 DeepSeek 返回的嵌套 JSON 数据——包括候选疾病列表、匹配度评分、建议就诊科室。这不是偷懒的做法因为模型输出结构不固定用TEXT存 JSON 比强行拆几十个字段更现实读取后再用 Jackson 解析成 Java 对象。ai_log表值得一提查问题的时候你能回看每一次模型调用哪些 prompt 导致了 token 浪费、哪些输入让模型回复了非预期内容一目了然。这是从业者的习惯很多毕设根本没有这个表。2.3 核心参数配置application.yml 里的模型与数据库连接解读后端src/main/resources/application.yml里除了常规的 MySQL 数据源、Redis 配置、JWT 密钥还有一个独立的deepseek配置段deepseek: api-key: ${DEEPSEEK_API_KEY:your-key-here} base-url: https://api.deepseek.com model: deepseek-chat max-tokens: 2048 temperature: 0.3 timeout: 60这段配置的意思是API Key 通过环境变量DEEPSEEK_API_KEY注入避免把密钥写死在代码里base-url是 DeepSeek 官方接口地址model指定使用deepseek-chat这是 DeepSeek 对外提供的通用对话模型支持显式上下文max-tokens控制单次返回的最大 token 数temperature0.3是为了让输出更稳定、更少随机。医疗场景不像写诗不需要高温度值温度调低一点模型输出的可预测性高很多timeout设成 60 秒是考虑到长文本病历生成时模型推理时间可能超过普通 HTTP 请求的默认超时。数据库连接部分你根据自己的 MySQL 版本改url、username、password就行。资源自带的是 MySQL 5.7 适用的建表语句如果要跑在 MySQL 8.0注意默认密码插件不同驱动版本选对即可。3. DeepSeek 接入实战从 HTTP 封装到流式输出的完整链路3.1 AI 接口封装用 WebClient 调 DeepSeek Chat Completion 接口后端 AI 模块核心代码是封装了 DeepSeek 的 Chat Completion 接口。这里不用 RestTemplate因为它是同步阻塞模型高并发场景下容易吃满 Tomcat 线程。SpringBoot 3 自带的 WebClient 走响应式链路单次调用不阻塞业务线程。下面这段是资源里DeepSeekClient.java的核心逻辑精简版Service public class DeepSeekClient { private final WebClient webClient; private final DeepSeekProperties properties; public DeepSeekClient(DeepSeekProperties properties) { this.properties properties; this.webClient WebClient.builder() .baseUrl(properties.getBaseUrl()) .defaultHeader(Authorization, Bearer properties.getApiKey()) .defaultHeader(Content-Type, application/json) .codecs(configurer - configurer.defaultCodecs().maxInMemorySize(10 * 1024 * 1024)) .build(); } public String chatCompletion(ListChatMessage messages) { MapString, Object requestBody new HashMap(); requestBody.put(model, properties.getModel()); requestBody.put(messages, messages); requestBody.put(temperature, properties.getTemperature()); requestBody.put(max_tokens, properties.getMaxTokens()); requestBody.put(stream, false); return webClient.post() .uri(/chat/completions) .bodyValue(requestBody) .retrieve() .bodyToMono(String.class) .timeout(Duration.ofSeconds(properties.getTimeout())) .block(); } }逻辑说明WebClient在构造时读配置里的地址和 Key每次调用走POST /chat/completions。关键参数messages是数组每个数组元素带rolesystem / user和content这是 DeepSeek 接口识别对话角色的标准方式。system角色用于设定 AI 行为user角色用于传用户输入。streamfalse表示非流式返回整体把模型回答一次性拿回来适合后期解析 JSON如果做前端打字机效果需要改用流式streamtrue返回 SSE并在前端用fetch读ReadableStream。参数说明maxInMemorySize别小看不配的话默认 256KB病历生成动辄几千 token 很容易超限报错这也是一个典型坑。block()方法在 WebClient 里是同步等待好在timeout兜底了最长等待时间。3.2 Prompt 构建与业务解析让模型按 JSON Schema 返回调通接口只是第一步真正难的是让模型输出“可解析的”结构化内容。源码里的PromptBuilder.java把整个 prompt 模板和 JSON Schema 固定下来我看完之后强烈建议你直接抄这份模板而不是自己现造。public String buildSymptomCheckPrompt(String userInput) { return 你是一名具有丰富临床经验的导诊医生。 用户的症状描述如下 userInput 请严格按以下 JSON 格式回复不要输出任何多余文字 {\candidate_diseases\: [{\disease_name\: \疾病名称\, \probability\: 0.85, \department\: \建议就诊科室\}], \questions\: [\为确认诊断你需要追问患者的问题1\, \追问问题2\], \urgent\: false}; }逻辑说明system提示词固定了“医生”角色user内容是对用户输入的封装。JSON Schema在 prompt 里的作用是告诉模型输出结构——候选疾病数组带疾病名、概率、科室追问问题数组以及一个urgent布尔值表示是否需要立即急诊。这个结构要和前端 Vue 页面里的v-for列表相对应。参数说明probability用 0-1 小数方便前端渲染百分比urgent字段直接驱动分诊逻辑——为 true 时界面跳出红色警示条。candidate_diseases数组长度最好限制 3 到 5 个太多会让患者焦虑太少参考价值低。解析端用 Jackson 把模型返回字符串反序列化成SymptomCheckResult对象如果解析失败则走兜底逻辑。兜底逻辑很重要模型偶尔会输出纯文本或 Markdown直接转对象会抛异常项目里专门写了一个fallbackParse()把文本按回车拆成临时疾病描述保证用户永远看得到反馈而不是报错白屏。3.3 流式输出的可选实现SSE 让病历生成像“打字机”一样展示资源里的非流式实现已经够完成毕设但如果你想拔高亮点可以把病历生成改成走 SSEServer-Sent Events。很多现场演示翻车就是因为点击“生成病历”后界面白等 10 秒没反馈评委以为系统死掉了。改成流式以后用户能实时看到模型吐字体验完全不同。后端在DeepSeekClient里加一个流式方法走WebClient的retrieve().bodyToFlux(String.class)响应体是text/event-stream格式前端用原生EventSource或fetch读流。核心改动就两点请求体里streamtrue以及前端不再等完整 JSON而是每收到一个data:块就 append 到页面。但这有个代价——流式输出没法保证一次返回完整 JSON所以自动生成结构化病历不能直接用流式结果入库比较合理的做法是“流式展示 完整返回入库”双通道前端展示用流式接口落库用非流式接口二次调用。资源里非流式版本已经能跑通全流程我建议先跑通再去改流式。4. 症状自查与结构化电子病历生成把模型输出变成业务资产4.1 症状自查模块前端输入与后端结果剪裁前端页面SymptomCheck.vue的思路很简单一个多行文本输入框用户用自然语言描述症状比如“最近三天头晕、头痛、恶心伴随腹泻”提交后后端把这段文本连同病历上下文拼进 prompt发给 DeepSeek返回候选疾病概率列表和追问问题。页面核心代码如下template div classsymptom-check el-input typetextarea v-modelsymptomText placeholder请描述你的症状比如头痛、发烧、咳嗽持续两天 :rows4 / el-button typeprimary :loadingloading clicksubmitSymptom {{ loading ? AI 分析中… : 开始自查 }} /el-button div v-ifcheckResult classresult-panel div v-fordisease in checkResult.candidate_diseases :keydisease.disease_name span{{ disease.disease_name }}/span span{{ Math.round(disease.probability * 100) }}%/span el-tag{{ disease.department }}/el-tag /div /div /div /template script setup import { ref } from vue import { checkSymptom } from /api/ai const symptomText ref() const loading ref(false) const checkResult ref(null) async function submitSymptom() { loading.value true try { const res await checkSymptom({ text: symptomText.value }) checkResult.value res.data } finally { loading.value false } } /script逻辑说明checkSymptom调用后端/api/ai/symptom-check后端返回的对象里有candidate_diseases、questions、urgent三个字段。模板里用v-for循环候选疾病并展示概率和科室如果urgent为 true再加一条el-alert提示紧急情况。这个页面在演示时非常出效果尤其是概率数字滚动出现的时候。参数说明:loading在请求期间锁住按钮防止重复提交接口层要加错误处理因为 DeepSeek 偶尔超时超时后给用户提示“网络不稳定请重试”。千万不要把模型超时错误直接透传到界面看起来很不专业。4.2 结构化电子病历从听诊记录到字段落库医生端“生成病历”功能的核心价值是在医生填完主诉和现病史后系统能帮他把这些自然语言文本自动扩展成一份可查询的病历草稿。源码里AutoGenerateMedicalRecord()这个服务的逻辑是拼一个要求模型“生成结构化 SOAP 格式病历”的 prompt然后把模型输出按{subjective: 主诉, objective: 体格检查, assessment: 诊断依据, plan: 治疗建议}的 JSON 结构拆开落到medical_record表对应的字段里。需要注意模型生成的内容再完整也不能直接盖过医生手写结论。资源设计里有个很聪明的点AI 生成的内容只作为“草稿”预填到表单医生最终要点击“确认保存”后才会落库。这避免了“AI 写的诊断被当成医生确诊”这种在医疗场景里不可接受的错误。如果你在答辩时被问到“AI 出错怎么办”这个设计就是你最好的回答模型只是辅助录入权威性在医生手里。落库后病历数据会被整理成record_view视图关联患者档案、处方、检查单前端“历史病历”页面直接查这些关联表还能做关键词搜索。这套数据模型虽然不是国标级别的结构化但足以支撑完整的业务演示。4.3 AI 日志与 token 成本控制别让演示崩在余额上面一次症状自查平均消耗 300-500 token一次完整病历生成可能消耗 1000-2000 token。如果你把 demo 演示沓三五次账户余额就肉眼可见地往下掉。项目里的ai_log表除了记内容还记录每次调用消耗的 token 数和耗时。我建议在调通后把max-tokens从 2048 降到 1024并给 prompt 里加一句“控制在 300 字以内”对毕设演示完全够用成本直接砍半。另外要警惕循环调用。后端接口没有做 Redis 限流如果被频繁刷请求AI 费用会疯涨。准备在公网演示时一定要在网关或过滤器层做一个基于用户 ID 的简单限流同一个用户一分钟最多调用 10 次 AI 接口。代码量不大但能防止你演示现场被无意识 F5 刷爆。5. 三个必踩的坑和我的排查记录从接口 401 到 JSON 解析失败5.1 现象一DeepSeek 接口一直返回 401 认证失败第一次启动项目ai_log表里出现一堆 401 错误。排查后发现是application.yml里${DEEPSEEK_API_KEY:your-key-here}这个配置的问题——本地环境变量没设配置直接读成了字面量your-key-here密钥当然无效。解决方法是确保启动脚本中export DEEPSEEK_API_KEYsk-xxxx或者用 IDE 环境变量面板注入。血泪经验写配置时别把假 Key 放在默认值位置宁可留空并在启动时校验。5.2 现象二病历生成偶尔出现“半截 JSON”导致前端白屏有一次演示点击生成病历后前端一直转圈控制台报JSON parse error。原因是请求超时设置太短模型生成到一半被timeout掐断返回了不完整的 JSON。解决方法是把timeout从 30 秒提高到 60 秒并在后端加一个“解析失败→返回友好提示”的兜底。从那以后我每次改 prompt 模板都强制跑一遍“长文本 短超时”两个边界用例。5.3 现象三前端 axios 拿不到 AI 接口的响应头页面可以通过api/ai/*正常拿数据但用CrossOrigin配的 CORS 拦不住自定义 header。排查发现是网关层没有把Authorization头透传给前端跨域预检请求。解决方式是使用 Spring 官方CorsFilter配置显式允许Authorization和Content-Type两个请求头。如果你用的是网关记得在路由配置里把这两个头加入 allowedHeaders。5.4 现象四JWT 在 Redis 里过期后用户下次操作仍显示“登录过期”因为资源里 JWT 没有接 Redis 统一校验。用户已登录期间如果 Redis 重启token 验证照样通过因为 JWT 是无状态签名服务端不存储会话。解决方法是把 Redis 缓存放进 JWT 拦截器里做二次校验——查user_session表token 对应记录为空直接返回 401。这是一个在答辩演示时非常容易出丑的坑务必提前处理。5.5 现象五MySQL 5.7 建 SQL 跑在 MySQL 8.0 时报时区错误这个问题是环境差异不是代码问题。MySQL 8.0 默认时区与 5.7 不同启动报The server time zone value xxx is unrecognized。解决方法是连接串加上serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue。也就是每次拆这种带数据库的源码第一件事先看 MySQL 版本和驱动版本是否匹配。6. 把这个系统做亮的三个进阶技巧答辩演示的临门一脚6.1 用“追问闭环”让症状自查更有医疗逻辑原始资源的症状自查是单轮问答输入症状 → 返回结果。我后来自己加了一个追问闭环每当返回候选疾病时同时把questions数组展示在界面底部让用户再回答一轮“头痛持续多久了有没有呕吐”把第二次问答的结果拼进初始文本再次调用模型。这样一来交互深度明显提升答辩时你能现场演示“AI 主动向用户提问”这比一次输到底的方案更能体现业务思考。实现起来不难前端维护一个conversationHistory用数组存{role: user | assistant, content}提交时把所有聊天记录一起传给后端后端全部塞进messages。模型会基于上下文修正诊断比每次只传一句话准确得多。6.2 通过ai_log做“费用看板”展示系统可观测性把ai_log表里的token_count和cost_estimate聚合统计在管理员页面做一个简单的折线图——按天展示 AI 调用次数和 token 消耗。这一步代码量不大但答辩时你告诉评委“系统能定量观测 AI 资源消耗”瞬间拉开和普通管理系统的差距。具体做法很直接后端写一个/api/ai/dashboard接口SQL 里GROUP BY DATE(create_time)并按 token 求和前端用 ECharts 画折线。既然资源里已经存了日志这个功能只是三小时工作量。6.3 用 SSE 把病历生成的“打字机”效果做出来前面提过流式输出的价值。具体落地时前端不要用EventSource因为 DeepSeek 接口需要自定义 Header。用原生fetch发 POST 请求读取response.body.getReader()每次读到一段字符就 append 到目标div。核心代码段如下const response await fetch(/api/ai/medical-record/stream, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ caseId }) }) const reader response.body.getReader() const decoder new TextDecoder() while (true) { const { done, value } await reader.read() if (done) break contentText.value decoder.decode(value, { stream: true }) }逻辑说明reader.read()每次返回一个 chunk用TextDecoder转成字符串后直接追加。这样页面会看到逐字输出虽然不是逐 token 平滑打字但也有明显的流式效果。后端在流式接口里要注意关闭响应缓冲否则前置的压缩或缓冲组件会让流式失效这是一个反复出现的坑。每当我拆到类似的 AI 业务系统我最先确认的就是三件事模型输出有没有留痕日志表、失败时用户看到什么兜底文案、以及数据是否被不可控内容污染。从那以后我跑这套系统第一件事是拿“长文本症状 短超时”和“空输入”两个用例把 AI 接口打一遍确认不会白屏然后才敢正式演示。这份资源最大的价值不只是能跑通的代码而是把大模型怎么融入传统业务系统的工程思路完整演示了一遍。希望这套源码和你照着做的过程能帮你顺利通关毕业答辩也能让你以后做类似功能时少踩我踩过的那些坑——用它当骨架把业务细节补成你自己的作品祝你好运。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站