一、引言

经过数周的持续开发与迭代,智愈医疗系统(Smart Medicine)已按计划完成全部核心功能的设计、编码、测试与部署。本文作为整个项目的收官博客,将从以下三个方面进行总结:

  1. 功能模块总览 — 回顾系统实现的全部功能,按模块分类说明
  2. 系统测试 — 功能测试、集成测试、兼容性测试的结果与问题修复
  3. 项目结项 — 交付物清单、技术指标达成情况、经验总结

二、功能模块总览

系统共实现 多个核心功能模块,覆盖"诊前查询 → 诊中分析 → 诊后管理 → 在线支付"的完整就医闭环。

2.1 智能医生对话系统

这是本项目的核心创新功能,实现了类似 DeepSeek、ChatGPT 等大模型产品的多轮对话体验。

主要功能:

  • 基于通义千问大模型的医疗咨询对话,提供 7×24 小时在线问诊
  • SSE 流式输出(EventSource 逐 token 呈现),用户可实时看到 AI 的思考过程
  • 多会话管理:左侧边栏展示历史对话列表,支持新建、切换、删除会话
  • 会话上下文隔离:每个会话的对话历史独立存储,AI 严格按会话读取上下文,不同会话互不干扰
  • 快捷追问:AI 回复下方显示"用药建议/预防建议/饮食建议/康复周期"四个快捷按钮,一键追问
  • 会话标题自动生成:取首条用户消息的前 50 字作为标题
  • 流式停止控制:输出过程中发送按钮变为"■ 停止",点击可随时中断 AI 回复
  • 后台流缓冲:切换会话时原 AI 流在后台继续运行,切回时可实时看到已累积的回复内容

技术实现:

前端 (EventSource) ↔ 后端 (SseEmitter) ↔ DashScope SDK (streamCall) ↔ 通义千问

核心代码 — 流式端点异步处理用户消息并保存到数据库:

@GetMapping("/query/stream")
public SseEmitter queryStream(
        @RequestParam String content,
        @RequestParam String conversationId,
        HttpSession session) {
    User loginUser = getLoginUser(session);
    chatHistoryService.saveMessage(loginUser.getId(), conversationId, Role.USER.getValue(), content);
    List<Message> messages = buildAIMessages(conversationId, loginUser.getId(), null);

    SseEmitter emitter = new SseEmitter(180000L);
    executor.execute(() -> {
        apiService.queryStream(messages,
            token -> emitter.send(SseEmitter.event().data(token)),
            () -> {
                chatHistoryService.saveMessage(userId, conversationId, Role.ASSISTANT.getValue(), fullResponse.toString());
                emitter.complete();
            },
            e -> emitter.completeWithError(e));
    });
    return emitter;
}

2.2 诊断书 AI 分析模块

实现了诊断书的异步上传与分析全流程,包含文件存储、异步任务调度、AI 识别、状态追踪。

主要功能:

  • 文件上传:支持 JPG/PNG/JPEG/PDF 格式,大小限制 10MB
  • 阿里云 OSS 存储:私有权访问 + Presigned URL 签名(有效期 30 分钟)
  • 异步任务队列:ThreadPoolTaskExecutor 线程池(core=2, max=4),提交即返回 taskId
  • 五状态状态机:PENDING → PROCESSING → SUCCESS / WARNING / FAILED
  • 指数退避重试:10s → 30s → 60s,最多 3 次
  • 前端轮询进度:每 2 秒轮询 /api/analysis-history/status
  • 分析结果结构化展示 + PDF 报告导出
  • 历史记录管理(列表/详情/删除/清空/批量删除/存储统计)

状态机设计:

                   ┌──────────┐
                   │ PENDING  │
                   └────┬─────┘
                        │ 线程池调度
                   ┌────▼─────┐
            ┌──────│PROCESSING│
            │      └────┬─────┘
            │           │
     ┌──────▼──┐  ┌─────▼────┐  ┌────────┐
     │ SUCCESS │  │ WARNING  │  │ FAILED │
     │ (成功)  │  │(部分识别) │  │ (失败)  │
     └─────────┘  └──────────┘  └────────┘

2.3 疾病与药品知识库

  • 疾病分类管理:支持 11 个科室分类,按分类浏览疾病
  • 疾病详情页:病因、主要症状、特殊症状、关联药品推荐
  • 药品信息:品名、关键字、功效、品牌、相互作用、禁忌、剂型、价格
  • 多维度搜索:按疾病名称、症状关键词、药品名称等搜索
  • 浏览量追踪:记录每个疾病的查看次数

三、系统测试

3.1 功能测试

模块 测试项 结果 备注
用户登录 正常登录、密码错误、空账号 ✅ 通过
用户注册 邮箱验证码、重复账号检测 ✅ 通过
智能医生 多轮对话、上下文记忆 ✅ 通过 经测试 10 轮内上下文准确
流式输出 逐 token 渲染、停止按钮 ✅ 通过
多会话切换 创建/切换/删除/标题生成 ✅ 通过
快捷追问 四个按钮均正常触发追问 ✅ 通过
诊断书分析 图片上传、异步分析、结果展示 ✅ 通过 图片/PDF 格式均测试
进度轮询 前端 2s 轮询状态更新 ✅ 通过
重试机制 失败任务手动重试 ✅ 通过
PDF 导出 诊断报告 PDF 生成与下载 ✅ 通过
支付流程 统一下单 → 扫码 → 回调处理 ✅ 通过 沙箱环境
药品查询 名称搜索、分类浏览 ✅ 通过
反馈提交 意见反馈保存 ✅ 通过

3.2 Bug 修复记录

B1 — EventSource 连接丢失

  • 问题:流式请求 URL 拼接了 conversationId 但 EventSource 创建时用了不含该参数的硬编码 URL,后端返回 400 导致连接立即失败
  • 修复new EventSource(streamUrl) 使用正确拼接的完整 URL
  • 文件custom.js

B2 — 跨会话 AI 回复被误删

  • 问题:删除消息对时仅按 userId 过滤,未限制 conversationId。切换到新会话后,若 editMessageIds 中的 ID 属于旧会话,删除时会误删旧会话的消息
  • 修复deleteByIds() 增加 conversationId 参数,SQL 中追加 AND conversation_id = ? 双重隔离
  • 文件ChatHistoryService.java

B3 — 切换会话后原 AI 回复丢失

  • 问题:切换会话时 close() 关闭了 EventSource,后台线程虽保存了 AI 回复,但用户切回时无通知机制,必须手动刷新
  • 修复:切换时不关闭 EventSource,标记为 _abandoned 仅解引用。引入 window.streamBuffers 缓冲区,切回时从缓冲区实时渲染
  • 文件doctor.htmlcustom.js

B4 — 终端中文乱码

  • 问题:Windows Git Bash 终端编码 GBK,JVM 输出 UTF-8 导致中文日志显示乱码
  • 修复application.yml 中配置 logging.charset.console: GBK
  • 文件application.yml

B5 — 删除所有会话后重复创建

  • 问题deleteConversationnewConversation() 内部已调用 loadConversationList(),外层又调一次,两个 AJAX 并发执行导致创建两个新会话
  • 修复:区分当前会话删除与非当前会话删除,只在非当前会话删除时单独刷新列表
  • 文件doctor.html

B6 — 实体类主键未使用包装类

  • 问题IllnessKindPageview 实体的 id 字段使用原始类型 int 而非包装类型 Integer
  • 修复int idInteger id,避免默认值 0 的问题
  • 文件IllnessKind.javaPageview.java

3.3 编码规范修复

对所有 Controller 和 Service 层进行了 Eclipse 空安全检查,修复了以下问题:

问题类型 修复方式 涉及文件
@NonNull 缺失 addViewControllers(@NonNull ViewControllerRegistry) MvcConfig.java
Bean 命名冲突 @Resource 字段名从 historyService 改为 analysisHistoryService TaskService.java
Null 类型安全转换 增加 token == null 守卫 + 局部变量提取 MessageController.java

四、技术指标达成情况

4.1 功能覆盖

模块 计划功能 实现功能 扩展功能 完成度
诊断书分析 5 项 5 项 批量删除、存储统计 100%
智能医生对话 4 项 6 项 多会话、快捷追问、停止控制 100%
疾病药品查询 3 项 3 项 100%
支付集成 3 项 3 项 100%
用户管理 3 项 3 项 100%

4.2 代码规模

指标 数值
Java 代码文件 50+ 个
页面模板 18 个
数据库表 13 张
RESTful API 端点 30+ 个
核心 JavaScript ~700 行

4.3 质量评分(ISO/IEC 25010)

维度 评分 说明
功能适用性 4.5/5 需求覆盖全面,扩展功能超出预期
性能效率 4.0/5 异步架构优化良好,流式输出延迟可控
可用性 4.5/5 多会话边栏、快捷追问提升交互效率
可靠性 4.5/5 异步重试、状态机保障数据一致性
安全性 4.0/5 OSS 签名 URL、XSS 防护、参数校验

五、经验总结

6.1 技术收获

  1. 异步架构设计:从同步阻塞到异步队列的改造,深刻理解了"提交即返回、状态可追踪、失败可重试"的设计理念。内存队列 + 线程池在单体应用中性价比极高。

  2. SSE 流式交互:EventSource + SseEmitter 的组合实现了类 DeepSeek 的实时输出体验。流式设计中需特别注意连接生命周期管理、中断恢复、缓冲区同步等问题。

  3. 状态机实践:五状态模型配合指数退避重试,有效提升了系统的容错性。WARNING 状态的引入为部分识别场景提供了更友好的用户体验。

  4. 多会话架构:会话表 + 消息表的分离设计支持独立上下文管理,AI 上下文严格按 conversation_id 隔离的设计经验可推广到任何多轮对话系统中。

6.2 项目管理收获

  1. 迭代增量开发:三次迭代逐步扩展功能,每次迭代都产出可运行版本,有效降低了项目风险。

  2. 问题驱动开发:开发过程中遇到的 Bug(EventSource 连接丢失、跨会话污染、编码问题等)都成为了改进系统的契机,每次修复都让系统更加健壮。

  3. 团队协作:Git 分支管理 + 代码审查机制保障了多人协作的代码质量。遇到合并冲突时通过沟通确认而非强行覆盖。

6.3 改进方向

方向 当前方案 改进方案
任务队列 内存队列 + 线程池 引入 RabbitMQ 实现持久化队列
文档格式 图片 + PDF 支持 Word、DICOM 医学影像
缓存 引入 Redis 缓存热点数据
移动端 桌面端为主 移动端适配 / 响应式改造
部署 本地运行 Docker Compose 容器化部署

七、结项语

智愈医疗系统从最初的需求分析、架构设计,到逐步编码实现、测试修复,再到最终的功能完善与结项,走完了软件项目管理的完整生命周期。系统实现了从诊断书上传、AI 识别分析、智能医患对话到在线支付的完整闭环,覆盖了患者就医的核心场景。

本项目的意义不仅在于产出了一个可运行的医疗辅助系统,更在于通过实践验证了异步架构状态机设计流式交互等技术方案在 Web 应用中的有效性。希望后续开发者能在此基础上继续迭代,打造更好的产品。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐