西藏巴青项目

在线接诊(兽医)技术方案.md 18KB

在线接诊(兽医)— 技术方案

依据:在线接诊(兽医)功能需求.md(v1.1);关联 AI诊断(兽医、机构)功能需求-草稿.md、原型
本文档版本:1.1(评审补充约定,见 §3.4、§4.6、§5、§8


1. 技术架构

说明
整体 RuoYi v3.9.2springboot2 分支)单体后端 + 若依 Vue2 管理端 + 移动端(App/小程序)
运行时 JDK 8、Spring Boot 2.x、Spring MVC、MyBatis、Druid
数据库 MySQL 5.7.39,InnoDB,utf8mb4
近实时 STOMP over WebSocket + 落库后推送(见 §5);端到端 ≤ 1 秒
文件 复用 POST /common/upload(见 §5.2);消息表存 URL;音视频仅文件上传
兽医资源 biz_medical_resourceresource_type=1sys_user_id 绑定兽医)
预约 本期不关联 biz_service_appointment(不校验预约状态)

后端分层(兽医在线接诊)

职责
Controller 会话列表、历史消息、发送消息;@PreAuthorize
WebSocket 订阅会话频道;新消息推送列表摘要与聊天区
Service 解析当前兽医、会话 scope、vet_visible 过滤、消息发送
Mapper + XML biz_consult_session / biz_consult_message
support ConsultSessionRulesVetConsultValidationVetProviderResolver(复用 diagnosis)

代码包com.ruoyi.web.modules.diagnosis
初始化脚本sql/biz_consult_session.sqlsql/biz_consult_message.sql(与 AI 诊断共用


2. 关联方案设计(在线接诊 / AI 诊断)

2.1 统一问诊 IM 数据模型

兽医在线接诊AI 诊断 共用两张表,以 consult_type 隔离;禁止混查。

consult_type 模块 会话参与方 后台新建会话
1 在线接诊(兽医) 问诊人(牧民)↔ 接诊兽医 (移动端创建/续聊)
2 AI 诊断(兽医、机构) 提问人(当前登录用户)↔ AI 助手 (后台「新增会话」)
3 专家在线问诊(预留) 问诊人 ↔ 专家 移动端(另模块)

2.2 三端能力对照

能力 兽医在线接诊 AI 诊断
Base Path /diseaseTreatment/onlineConsult/vet /diseaseTreatment/onlineConsult/ai
使用角色 兽医(100) 兽医 + 机构(102 等)
列表筛选项 askerNamecontentKeyword contentKeyword
默认列表 最近 3 个月 last_message_time 同左
超窗检索 搜索命中更早会话 同左
列表主标题 askerName sessionTitle
右侧标题 askerName 固定「AI 助手」
发送方 人 ↔ 人 人 → AI;AI 回复由 AI 服务写入(另接)
vet_visible 牧民删除后 0,兽医列表不可见 不适用(无接诊方)

2.3 近实时推送(共用)

消息落库 → 更新 session.last_message_* 
         → WebSocket 推送至:
            - 兽医后台:/topic/consult/vet/{vetUserId}
            - 移动端问诊人:/topic/consult/user/{askerUserId}
            - AI 提问人:/topic/consult/ai/{askerUserId}

客户端收到推送后刷新当前会话消息与(若相关)左侧列表摘要。

2.4 与「我的预约(兽医)」

  • 无表关联无接口校验预约状态。
  • 预约与问诊 IM 独立产品能力

2.5 AI 诊断接口摘要(关联实现)

# 说明 Method URI(示例)
A1 历史会话列表 GET /ai/session/list
A2 新增会话 POST /ai/session
A3 消息列表 GET /ai/session/{id}/messages
A4 用户提问(触发 AI 回复) POST /ai/session/{id}/message

AI 回复消息由异步任务调用大模型后 INSERTsender_role=AI),推送至提问人;不在兽医接诊 Controller 内实现。


3. 数据库设计

3.1 表 biz_consult_session(问诊会话)

字段 类型 非空 说明
id bigint(20) Y 主键
consult_type tinyint(4) Y 1兽医在线 2AI 3专家(预留)
asker_user_id bigint(20) Y 问诊人/提问人 sys_user_id
asker_name varchar(64) Y 问诊人姓名快照
receiver_user_id bigint(20) N 接诊人 sys_user_id(AI 会话可为 NULL)
receiver_provider_id bigint(20) N 兽医/专家 biz_medical_resource.id(AI 为 NULL)
receiver_name varchar(64) N 接诊人姓名快照
session_title varchar(200) N 问题/问答标题(AI 列表主标题)
real_session_id varchar(64) N 大模型网关 session_id(AI 诊断落库写入,见 AI 诊断方案 §3.1)
last_message_time datetime N 最后一条消息时间(列表排序、3 个月窗)
last_message_preview varchar(500) N 列表摘要(文本截断或 [图片] 等)
vet_visible tinyint(4) Y 1兽医可见 0牧民删除后对兽医隐藏(仅 consult_type=1 使用)
create_time datetime N 创建时间
update_time datetime N 更新时间

索引

  • PRIMARY KEY (id)
  • UNIQUE KEY uk_vet_session (consult_type, asker_user_id, receiver_provider_id)consult_type=1receiver_provider_id 非空时保证一对一)
  • KEY idx_receiver (consult_type, receiver_user_id, receiver_provider_id, last_message_time)
  • KEY idx_asker (consult_type, asker_user_id, last_message_time)
  • KEY idx_vet_visible (consult_type, vet_visible, last_message_time)

DDL:见 sql/biz_consult_session.sql

3.2 表 biz_consult_message(问诊消息)

字段 类型 非空 说明
id bigint(20) Y 主键
session_id bigint(20) Y 会话 ID
sender_role tinyint(4) Y 1问诊人/提问人 2接诊人/兽医 3AI 助手
sender_user_id bigint(20) N 发送方用户 ID(AI 可为 0)
sender_name varchar(64) N 发送方姓名快照
msg_type tinyint(4) Y 1文本 2图片 3视频 4语音
content text N 文本内容或媒体 URL
media_duration int(11) N 语音/视频时长(秒,可选)
ai_category varchar(32) N AI 提问分类(仅 AI 诊断提问人消息,见 AI 诊断方案 §3.2)
cost_time bigint(20) N 大模型耗时毫秒(仅 AI 诊断 AI 消息,见 AI 诊断方案 §3.2)
send_time datetime Y 发送时间
create_time datetime N 创建时间

索引PRIMARY KEY (id)KEY idx_session_time (session_id, send_time)KEY idx_session_id (session_id)

DDL:见 sql/biz_consult_message.sql

3.3 兽医列表查询语义

默认列表(未点搜索):

WHERE consult_type = 1
  AND receiver_provider_id = #{vetProviderId}
  AND vet_visible = 1
  AND last_message_time >= #{threeMonthsAgo}
ORDER BY last_message_time DESC

搜索(仅当请求参数 searchMode=true 时生效,见 §4.6):

  • vet_visible = 1不限制 3 个月。
  • askerNameasker_name LIKE(非空时)。
  • contentKeyword → 匹配 last_message_previewsession_title,或子查询 biz_consult_message.contentmsg_type=1 文本,consult_type=1 同会话)。

3.4 实现约定(v1.1 已确认)

# 约定项 本期实现
1 列表「搜索 / 重置」 仅用户点击搜索时传 searchMode=true重置清空筛选项并传 searchMode=false(或未传),恢复默认 3 个月列表。筛选项有值但未点搜索时,仍按默认 3 个月查询
2 3 个月时间窗 服务器时区(Asia/Shanghai自然日threeMonthsAgo = 今日 00:00:00 往前推 3 个自然月(含今日当天有最后消息的会话)。例:今日 2026-05-20 → last_message_time >= 2026-02-20 00:00:00
3 last_message_preview 发送成功后由服务端写入:msg_type=1 取文本去空白后最多 50 字2/3/4 固定为 [图片] / [视频] / [语音]
4 列表头像 列表接口返回 askerAvatarsys_user.avatar;为空时前端用默认占位图(与原型一致)
5 历史消息首次加载 GET .../messages 未传 beforeId:按 send_time DESC 取最新 50 条,响应体内send_time 升序排列供聊天区展示;上拉更早传 beforeId(当前页最小 messageId),再取 50 条。详情区不受列表 3 个月限制
6 WebSocket 采用 STOMP(Spring WebSocket + SockJS 可选);事件类型 NEW_MESSAGE(含 §4.2 字段 + sessionIdlastMessagePreviewlastMessageTime 供左侧列表更新)。多实例:推送经 Redis Pub/Sub 广播至各节点再下发(若依集群部署时必配)
7 媒体上传 POST /common/upload,消息 content 存返回 url。限制:图片 jpg/jpeg/png/gif,≤ 10MB视频 mp4/mov,≤ 50MB语音 mp3/m4a/wav,≤ 10MB(与平台统一规范冲突时以平台为准)
8 迭代里程碑 兽医后台移动端 M1 打开会话、M2 发消息、M4 hide 同一迭代交付;缺移动端则后台仅可联调/造数,不作为上线验收

其他说明

  • i18n:后端存原文;界面汉语/藏文由前端 i18n,接口不单独传语言包。
  • 操作审计:本期不单独建审计表;若平台要求查看/发送留痕,复用若依 oper_log(后续迭代)。
  • AI 会话consult_type=2receiver_provider_id 必须为 NULL,禁止写入兽医资源 ID,避免误占 uk_vet_session 语义。

4. 接口设计(兽医在线接诊)

统一响应:RuoYi AjaxResult / TableDataInfo;WebSocket 见 §5

权限(示例)diseaseTreatment:vetOnlineConsult:list|query|send

Base Path/diseaseTreatment/onlineConsult/vet

| # | 说明 | Method | URI | 权限 | 要点 | | --- | --- | --- | --- | --- | | 4.1 | 接诊会话列表 | GET | /session/list | ...:list | Query:pageNumpageSize(默认 20)、askerNamecontentKeywordsearchMode仅「搜索」为 true,见 §4.6);服务端注入 receiverProviderId,禁止前端传 | | 4.2 | 历史消息 | GET | /session/{sessionId}/messages | ...:query | 校验会话归属与 vet_visible=1;未传 beforeId 取最新 50 条(§3.4 #5);传 beforeId 上拉更早;pageSize 默认 50;响应 rows 升序 | | 4.3 | 发送消息 | POST | /session/{sessionId}/message | ...:send | Body:msgType(1~4)、content(文本或上传后的 URL)、mediaDuration(可选);落库后 WebSocket 推送;更新 last_message_*update_time |

4.1 列表 rows[] 字段

sessionIdaskerNameaskerAvatarlastMessagePreviewlastMessageTime(前端格式化为「M月D日」)、sessionTitle(可选)。

4.2 消息 rows[] / 推送 payload 字段

messageIdsessionIdsenderRolesenderNamemsgTypecontentmediaDurationsendTime

senderRole:1 问诊人(左气泡)、2 兽医(右气泡)。

4.3 发送 Body 示例

{
  "msgType": 1,
  "content": "建议先隔离观察"
}

图片/视频/语音:msgType 为 2/3/4,content 为上传接口返回的 URL。

4.4 业务错误(msg 示例)

场景 文案
未绑定兽医 未绑定兽医资源,无法接诊
越权会话 无权查看该会话
会话不可见 会话不存在或已删除
文本为空 请输入消息内容
媒体为空 请上传文件
文件不合规 文件格式或大小不符合要求

4.5 与 AI 诊断接口差异

兽医 AI
列表 Path .../vet/session/list .../ai/session/list
新建会话 POST .../ai/session
发送 人发 → 人收 人发 → AI 处理 → AI 消息入库
vet_visible 使用 不使用

4.6 列表「搜索 / 重置」交互(与前端约定)

用户操作 请求参数 列表范围
进入页面 / 重置 searchMode=false(或不传);askerNamecontentKeyword 清空 仅最近 3 个月§3.4 #2
点击搜索 searchMode=true;可带 askerNamecontentKeyword(均可空,但至少一项非空否则提示「请输入筛选条件」) 全量可见会话(仍 vet_visible=1),可含 3 个月外
仅输入筛选项未点搜索 同默认 仍仅 3 个月§3.4 #1

5. 近实时、上传与部署

5.1 WebSocket(STOMP)

说明
协议 STOMP over WebSocket;管理端 SockJS 端点示例 /ws/consult
鉴权 握手携带与 HTTP 相同 Token;服务端绑定 userId
兽医订阅 /topic/consult/vet/{sysUserId}
牧民订阅 /topic/consult/user/{sysUserId}
推送事件 NEW_MESSAGE:payload = §4.2 消息字段 + sessionId + lastMessagePreview + lastMessageTime(左侧列表同步更新)
多实例 落库后发布至 Redis 频道 consult:push,各节点订阅后向本机 STOMP 连接推送
延迟 落库 → Redis/本机推送 → 客户端渲染,验收 ≤ 1 s§8

本期不做:已读回执、正在输入、消息撤回、独立 SESSION_SUMMARY 事件(摘要随 NEW_MESSAGE 携带即可)。

5.2 媒体上传

1. POST /common/upload(multipart)
2. 校验后缀与大小(§3.4 #7)
3. POST .../message  Body: { msgType, content: url, mediaDuration? }

发送失败时 HTTP 返回错误,msg§4.4向聊天区插入乐观消息。

5.3 菜单与权限(示例)

菜单名 权限标识 路由 component
在线接诊(兽医) diseaseTreatment:vetOnlineConsult:list diseaseTreatment/onlineConsult/vet/index

按钮:querysend。角色 100(兽医)绑定;机构/专家此菜单。


6. 移动端接口(与兽医后台同迭代)

# 说明 Method URI(示例) 要点
M1 打开/创建兽医会话 POST /app/onlineConsult/session/open Body:vetResourceIdconsult_type=1;写入 receiver_user_id/receiver_name(来自资源绑定账号);有则返回已有 sessionId
M2 发送消息 POST /app/onlineConsult/session/{id}/message 问诊人;sender_role=1;落库后同 §5.1 推送给兽医
M3 会话列表 GET /app/onlineConsult/session/list 牧民消息 Tab;按 asker_user_id=当前用户
M4 删除会话(对兽医隐藏) POST /app/onlineConsult/session/{id}/hide 校验 asker_user_id=当前用户vet_visible=0

里程碑(§3.4 #8:M1/M2/M4 与兽医后台 4.1~4.3 + WebSocket 同版本上线;M3 可与 M1 同批。

移动端控制器:BizAppOnlineConsultController/app/onlineConsult),详见 doc/app/在线问诊/在线问诊接口说明.md复用 IBizVetOnlineConsultService 核心逻辑。


7. 实现要点

说明
越权 所有 sessionId 操作校验 consult_type=1 + receiver_provider_id=当前兽医 + vet_visible=1
会话唯一 openSELECT 已有会话,避免重复建多条
媒体 发送前走统一上传;校验扩展名与大小(平台规范)
分页 §3.4 #5;聊天区升序;上拉 beforeId
摘要更新 每次发送成功后更新 last_message_timelast_message_preview§3.4 #3
组件路径 diseaseTreatment/onlineConsult/vet/index
AI 模块 同包 AiOnlineConsultControllerconsult_type=2,列表含 POST 新建会话

建议类

  • ConsultSessionRules(类型、角色、消息类型常量)
  • BizConsultSessionMapper / BizConsultMessageMapper
  • VetOnlineConsultServiceImpl
  • ConsultWebSocketHandler + 推送服务

8. 验收要点(v1.1)

编号 场景 通过标准
AC-01 近实时 兽医后台与移动端同时在线;任一端发文本,对端 ≤ 1 s 出现消息且左侧摘要更新
AC-02 默认列表 仅展示 last_message_time 在 3 个月窗内的会话;超窗会话不出现
AC-03 搜索超窗 对超窗会话点搜索searchMode=true)可命中;重置后该会话从默认列表消失
AC-04 牧民删除 hide 后兽医列表无该会话;详情/发消息返回「会话不存在或已删除」
AC-05 越权 兽医 A 无法访问兽医 B 的 sessionId
AC-06 媒体 图片/视频/语音文件上传发送成功;超大或非法后缀返回 §4.4 文案
AC-07 与 AI 隔离 兽医列表 SQL 仅 consult_type=1;AI 会话不出现在接诊列表

9. 交付清单

  • sql/biz_consult_session.sqlsql/biz_consult_message.sql
  • ConsultSessionRules + Mapper/XML(含 searchMode、3 个月窗、摘要写入)
  • VetOnlineConsultController + Service + STOMP + Redis 推送
  • 移动端 M1/M2/M4(同迭代,§3.4 #8
  • AiOnlineConsultController(关联模块,可次迭代;须 receiver_provider_id=NULL
  • 菜单权限 SQL(§5.3
  • 单元测试 + MockMvc + §8 联调用例

10. 修订记录

版本 说明
1.0 初稿:共用问诊 IM 表;兽医接口;WebSocket≤1s;3 个月默认可搜;vet_visible;AI 诊断关联路径;不绑定预约
1.1 评审补充:searchMode 与搜索/重置;3 个月自然日;摘要规则;askerAvatar;消息分页;STOMP+Redis;上传规范;移动端同迭代;验收用例 AC-01~07