西藏巴青项目

AI诊断(兽医、机构)技术方案.md 24KB

AI 诊断(兽医、机构)— 技术方案

依据:AI诊断(兽医、机构)功能需求.md(v1.1);关联 在线接诊(兽医)技术方案.md(v1.1)、在线接诊(兽医)功能需求.md
本文档版本:1.6biz_consult_user 使用用户表,见 §3.0


1. 技术架构

说明
整体 RuoYi v3.9.2springboot2 分支)单体后端 + 若依 Vue2 管理端 + 移动端
运行时 JDK 8、Spring Boot 2.x、Spring MVC、MyBatis、Druid
数据库 MySQL 5.7.39,InnoDB,utf8mb4
问诊 IM 表 复用 biz_consult_sessionbiz_consult_message(与在线接诊共用,见 §2);另增 biz_consult_user 记录提问人首次/末次使用时间(§3.0
近实时 复用 Redis Pub/Sub consult:push + STOMP(与在线接诊 §5.1 一致);提问落库即推;AI 回复落库后推,≤ 1 秒
文件 POST /common/upload;消息存 URL
AI 能力(对话框) 管理端/移动端 直连或经本服务转发 GET /v1/modelsPOST /v1/chat/completionscom.ruoyi.web.kb.controller.KbOpenAiProxyController,配置 ruoyi.kb);详见 doc/大模型/大模型网关转发接口说明.md、前端方案 §4
AI 能力(服务端兜底) ConsultAiGateway:仅当落库请求 未带 aiReplyContent 时由服务端调模型;超时 60s非流式整段回复
后台准入 若依菜单 + diseaseTreatment:aiOnlineConsult:* 权限;不校验 biz_medical_resource 绑定
预约/接诊 不关联 biz_service_appointment、在线接诊接口

后端分层(AI 诊断)

职责
Controller 后台 .../onlineConsult/ai;移动端 .../app/consult/ai
Service 列表/新建/消息/提问;调用 AI 生成回复;hide
ConsultAiGateway 文本/图片调用模型;视频/语音返回固定引导文案
Mapper + XML 扩展 BizConsultSessionMapperconsult_type=2 查询);BizConsultUserMapper§3.0
support 复用 ConsultSessionRulesConsultThreeMonthsWindowConsultMessagePreviewVetConsultValidation;新增 AiConsultValidation

代码包com.ruoyi.web.modules.diagnosis(与在线接诊、医疗资源同域)
大模型转发包com.ruoyi.web.kb(与知识库共用 ruoyi.kb,见 §1.2
DDLsql/biz_consult_session.sqlsql/biz_consult_message.sql(会话/消息,与在线接诊共用);sql/biz_consult_user.sql(AI 诊断使用用户,§3.0

1.2 大模型 OpenAI 兼容转发(/v1

与知识库同属大模型网关体系,实现类不在 diagnosis 包而在 kb 包:

对外路径 实现 用途
GET /v1/models KbOpenAiProxyControllerKbOpenAiProxyServiceImpl 模型列表(原样转发上游)
POST /v1/chat/completions 同上;stream:false JSON,stream:true SSE 管理端 AI 诊断、移动端 AI 助手 对话框问答
说明
鉴权(本服务) 须若依登录 TokenKbOpenAiProxyController@Anonymous);未登录 401
鉴权(上游) 服务端 ruoyi.kb.api-key 访问上游,使用客户端 Authorization
配置 ruoyi.kbscheme/host/port/api-key/stream-read-timeout-ms
与业务接口关系 会话 CRUD、消息落库仍走 .../onlineConsult/ai/**.../app/consult/ai/**;前端大模型返回后 POST .../messageaiReplyContent再走 ConsultAiGateway
设计/测试 doc/大模型/大模型网关转发接口说明.mddoc/大模型/大模型网关转发测试用例.md

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

2.1 统一数据模型(consult_type 隔离)

consult_type 模块 receiver_provider_id vet_visible 语义(本期)
1 在线接诊(兽医) 兽医 biz_medical_resource.id 牧民删除后对兽医不可见
2 AI 诊断 必须为 NULL 复用字段:提问人删除后对本人列表不可见(0
3 专家问诊(预留) 专家资源 ID 待定

禁止:AI 列表/消息 SQL 出现 consult_type=1;接诊 SQL 出现 consult_type=2

2.2 能力对照

能力 在线接诊(兽医) AI 诊断(本文档)
Base Path /diseaseTreatment/onlineConsult/vet /diseaseTreatment/onlineConsult/ai
角色 兽医(100) 兽医(100)+ 机构(102 等)
列表筛选 askerNamecontentKeyword contentKeyword
新建会话 POST /session
发送 人 → 人 人 → AI 服务 → AI 消息入库
列表标题字段 askerName sessionTitle
移动端 Path /app/onlineConsult /app/consult/ai

2.3 推送频道(共用基础设施)

提问/AI 回复落库 → 更新 last_message_* 
                → Redis consult:push 
                → STOMP /topic/consult/ai/{askerUserId}

Payload 同在线接诊 §4.2senderRole:1 提问人,3 AI 助手)。

2.4 与在线接诊代码复用

复用项 说明
biz_consult_* 同表异 consult_type
ConsultMessagePreview 摘要 50 字 / [图片]
ConsultThreeMonthsWindow 3 个月自然日
ConsultRedisPushService 推送
BizConsultMessageMapper 消息插入、分页查询
VetConsultValidation.validateSendBody 发送体校验(AI 模块复用)

独立实现BizAiOnlineConsultServiceImplBizAiOnlineConsultControllerConsultAiGatewayselectAiSessionList 等 Mapper 方法。

2.5 与「我的预约」

无表关联、无接口校验。


3. 数据库设计

会话/消息在共用表上按 consult_type=2 读写;另增 AI 诊断使用用户 表记录提问人首次/末次使用时间。

3.0 表 biz_consult_user(AI 诊断使用用户)

字段 类型 说明
user_id bigint(20) 主键;与提问人 asker_user_id 一致
first_use_time datetime 开始使用 AI 诊断时间
last_use_time datetime 最后一次使用 AI 诊断时间

写入规则

时机 行为
POST .../session 新建会话 user_id 不存在则 INSERTfirst_use_timelast_use_time 均为当前时间
更新 biz_consult_session.last_message_time 同步 UPDATE 对应 asker_user_idlast_use_time(与 last_message_time 一致)

DDLsql/biz_consult_user.sql

3.1 表 biz_consult_session(AI 会话写入约定)

字段 AI 会话取值
consult_type 2
asker_user_id / asker_name 当前登录用户
receiver_user_id NULL
receiver_provider_id NULL(禁止写入兽医/机构资源 ID)
receiver_name NULL
session_title 新建时 「新会话」;首条有效文本提问后更新为摘要(≤50 字)
real_session_id varchar(64),可为空;大模型网关 session_id,由 POST .../message 写入/更新,供多轮 session_id 回传
last_message_time / last_message_preview 每次提问或 AI 回复后更新
vet_visible 默认 1;提问人 hide 后置 0

索引使用idx_asker (consult_type, asker_user_id, last_message_time)

多会话:同一 asker_user_id 可有多行 consult_type=2使用 uk_vet_session 限制 AI;该唯一键仅对 receiver_provider_id 非空的兽医会话生效)。

3.2 表 biz_consult_message

sender_role 含义
1 提问人(兽医/机构/牧民)
3 AI 助手sender_user_id=0sender_name=AI助手

在线接诊使用的 2(接诊兽医)在 AI 会话中不出现

字段 说明
ai_category varchar(32),可为空;仅 提问人消息sender_role=1)写入;AI 回复为 NULL。纯数字时须为 14(与 AiConsultConstants 分类一致),亦可存业务编码字符串
cost_time bigint,可为空;大模型对话耗时(毫秒),仅 AI 消息sender_role=3)写入;来自 /v1/chat/completions 响应头 X-COST
asker_ip varchar(128),可为空;提问人所属 IP,仅 AI 诊断提问人消息sender_role=1)写入;由 IpUtils.getIpAddr() 从当前 HTTP 请求解析

3.3 AI 问诊疫病预警定时任务

说明
Quartz aiConsultDiseaseWarningTask.scanAndWarn;cron 建议 0 0/30 * * * ?(每 30 分钟);SQL 见 sql/biz_sys_job_ai_consult_disease_warning.sql
统计 当月(Asia/Shanghai,send_time 从月初至当前)biz_consult_messagebiz_consult_sessionconsult_type=2),条件:sender_role<>3sender_user_id<>0ai_category=yak-disease(固定),按 asker_ip 计数
阈值 diagnosis.ai-consult-disease-warning.threshold(默认 3
地区 对达阈值 IP 调用太平洋 IP 库解析区县,展示为 pasture_name(如「巴青区」)
落库 biz_yak_disease_warningwarning_type=5data_source=ai_consult_analysis;同月同 pasture_name 已存在则 更新 alert_message/alert_time,否则 插入
文案 {区名}本月截止当前时间AI问诊记录集中({N}条),触发疫病预警。

已有库迁移sql/biz_consult_user.sql(或 sql/alter_rename_biz_diagnosis_user_to_biz_consult_user.sql);sql/alter_biz_consult_session_real_session_id.sqlsql/alter_biz_consult_message_ai_category.sqlsql/alter_biz_consult_message_cost_time.sqlsql/alter_biz_consult_message_asker_ip.sql

3.3 AI 列表查询语义

默认列表searchMode=false):

-- 时间字段见 §3.5 推荐 SQL(COALESCE 或创建时写入 last_message_time)
WHERE consult_type = 2
  AND asker_user_id = #{askerUserId}
  AND vet_visible = 1
  AND COALESCE(last_message_time, create_time) >= #{threeMonthsAgo}
ORDER BY COALESCE(last_message_time, create_time) DESC

搜索searchMode=true):

  • vet_visible = 1不限制 3 个月。
  • contentKeyword 非空时匹配 session_titlelast_message_preview、消息文本子查询(含 AI 回复文本)。

3.4 实现约定(对齐功能需求 v1.1)

# 约定项 实现要点
1 3 个月窗 ConsultThreeMonthsWindow.calcThreeMonthsAgo()Asia/Shanghai
2 搜索/重置 searchMode=true 超窗;空关键字 →「请输入筛选条件」
3 摘要 ConsultMessagePreview.build
4 首问标题 首条 msg_type=1 提问成功后 update session_title
5 消息分页 未传 beforeId 取最新 50 条,响应升序
6 隐藏 updateSessionVisiblevet_visible=0;管理端/移动端 POST .../hide,仅 asker_user_id=当前用户
7 AI 超时 ConsultAiGateway 调用 60s 超时,失败不插入 AI 消息
8 非流式 模型返回整段文本后一次 insert + 推送

3.5 实现约定补充(v1.1 已确认)

# 约定项 本期实现
1 空会话进列表 POST /session 创建时同步写入 last_message_time = create_time(或列表 SQL 使用 COALESCE(last_message_time, create_time) 参与 3 个月过滤与排序),保证「新会话」立即出现在默认列表
2 HTTP 读超时 POST .../message 同步等待 AI 最长 60s;Tomcat/网关 read timeout ≥ 65s,避免先于 AI 调用返回 504
3 视频/语音固定回复 ConsultAiGateway 常量文案(写入 AI 消息):已收到视频,请补充文字描述症状。 / 已收到语音,请补充文字描述。
4 图片识图失败 模型不可用或识图失败时,仍插入一条 AI 文本消息(sender_role=3),内容为:图片识别暂不可用,请补充文字描述症状。仅返回 HTTP 错误而无 AI 气泡
5 移动端搜索 GET /app/consult/ai/session/list 支持与后台相同参数:contentKeywordsearchMode(规则同 §3.3
6 AI 上下文 本期 单轮:每次仅将当前提问(文本或图片 URL + 可选说明)送模型;不带历史多轮上下文(多轮对话产品化另迭代)
7 同会话并发 同一会话在 AI 处理中(服务端标记或分布式锁)拒绝第二次 POST .../message,返回「AI 正在回复中,请稍候」
8 免责声明 POST /session 与首次 GET /session/list 响应附带字段 disclaimer(常量,与功能需求 §5.4 #6 一致);前端必须展示
9 vet_visible 口径 本文档 §2.1 为准:AI 会话复用该字段表示提问人列表可见;在线接诊技术方案中「AI 不适用」表述作废

列表 SQL 推荐(含空会话)

WHERE consult_type = 2
  AND asker_user_id = #{askerUserId}
  AND vet_visible = 1
  AND COALESCE(last_message_time, create_time) >= #{threeMonthsAgo}
ORDER BY COALESCE(last_message_time, create_time) DESC

4. 接口设计(后台 AI 诊断)

统一响应:AjaxResult / TableDataInfo

权限(示例)diseaseTreatment:aiOnlineConsult:list|query|send|add|remove

Base Path/diseaseTreatment/onlineConsult/ai

准入:登录 + 接口权限;列表/新建按 asker_user_id = 当前用户 隔离,调用 VetProviderResolver / OrgProviderResolver 校验医疗资源绑定。

| # | 说明 | Method | URI | 权限 | 要点 | | --- | --- | --- | --- | --- | | 4.1 | 历史会话列表 | GET | /session/list | ...:list | Query:pageNumpageSize(默认 20)、contentKeywordsearchModeasker_user_id 服务端注入 | | 4.2 | 新增会话 | POST | /session | ...:add | 创建 consult_type=2 空会话;session_title=新会话last_message_time=create_time§3.5 #1);返回 sessionIddisclaimer | | 4.3 | 历史消息 | GET | /session/{sessionId}/messages | ...:query | 校验 consult_type=2 + asker_user_id + vet_visible=1beforeIdpageSize(默认 50) | | 4.4 | 提问(触发 AI) | POST | /session/{sessionId}/message | ...:send | Body:msgTypecontentmediaDuration?aiReplyContent?realSessionId?aiCategory?costTime?(毫秒,来自 X-COST);落库时更新 last_message_*update_time;流程见 §5 | | 4.5 | 隐藏会话 | POST | /session/{sessionId}/hide | ...:remove | 管理端隐藏 AI 诊断会话;vet_visible=0;仅本人会话;隐藏后列表/消息/提问不可用 |

4.1 列表 rows[] 字段

sessionIdsessionTitlelastMessagePreviewlastMessageTimerealSessionId(大模型网关会话 ID,可为空)。

4.2 消息 rows[] / 推送字段

messageIdsessionIdsenderRole(1 提问人 / 3 AI)、senderNamemsgTypecontentmediaDurationaiCategory(可为空)、askerIp(可为空,仅提问人消息)、sendTime

4.3 提问 Body 示例

服务端调 ConsultAiGateway(无 aiReplyContent

{
  "msgType": 1,
  "content": "牛不吃草、精神沉郁,请问可能原因?"
}

前端大模型直连后落库(推荐)

{
  "msgType": 1,
  "content": "牛不吃草、精神沉郁,请问可能原因?",
  "aiReplyContent": "可能原因包括…",
  "realSessionId": "gateway-session-001",
  "aiCategory": "2",
  "costTime": 1523
}
Body 字段 类型 必填 说明
msgType int Y 1 文本 2 图 3 视频 4 语音
content string 条件 文本或媒体 URL
mediaDuration int N 音视频秒数
aiReplyContent string N 非空时服务端仅落库,不调 ConsultAiGateway
realSessionId string N 大模型 session_id;写入 biz_consult_session.real_session_id(别名 real_session_idsession_idllmSessionId
aiCategory string N 写入提问人消息 ai_category(别名 ai_categorycategory);最长 32
costTime long N 写入 AI 消息 cost_time(别名 cost_timexCost);毫秒,来自大模型响应头 X-COST

提问成功 data

{
  "userMessage": { "id": 1, "senderRole": 1, "aiCategory": "2", "...": "..." },
  "aiMessage": { "id": 2, "senderRole": 3, "...": "..." },
  "realSessionId": "gateway-session-001"
}

4.4 业务错误(msg 示例)

场景 文案
越权会话 无权查看该会话
会话不可见 会话不存在或已删除
搜索为空 请输入筛选条件
文本为空 请输入消息内容
媒体为空 请上传文件
文件不合规 文件格式或大小不符合要求
AI 超时 AI 回复超时,请稍后重试
AI 失败 AI 回复失败,请稍后重试
AI 处理中 AI 正在回复中,请稍候
AI 提问分类无效 AI提问分类无效 / AI提问分类长度不能超过32个字符

5. 提问与 AI 回复流程(核心)

1. 校验会话归属(type=2, asker=当前用户, vet_visible=1)
2. 若会话 AI 处理中 → 拒绝(§3.5 #7)
3. 校验 Body(VetConsultValidation、AiConsultValidation.validateAiCategory)
4. 若 Body.realSessionId 非空 → 更新 biz_consult_session.real_session_id
5. 标记会话 processing=1(内存/Redis 锁)
6. INSERT 提问消息(sender_role=1,含 ai_category 若有)
7. 更新 session.last_message_*(提问侧摘要)
8. 推送 NEW_MESSAGE(提问)→ 前端立即展示
9a. 若 aiReplyContent 非空:直接作为 AI 文本,跳过 ConsultAiGateway(前端直连大模型落库通道)
9b. 否则调用 ConsultAiGateway(≤60s,单轮 §3.5 #6):
   - msg_type=1:文本问答
   - msg_type=2:图文;失败 → §3.5 #4 固定 AI 文案仍入库
   - msg_type=3/4:§3.5 #3 固定引导文案入库
10. 成功:INSERT AI 消息(sender_role=3,ai_category=NULL)
11. 更新 session.last_message_*;首问更新 session_title
12. 推送 NEW_MESSAGE(AI 回复,落库后 ≤1s)
13. 释放 processing;响应 data:{ userMessage, aiMessage, realSessionId? }
14. 网关硬失败(非图片软失败且无 aiReplyContent):不写 AI 消息;HTTP 错误 + §4.4 文案

5.1 同步、超时与展示策略(v1.1 已确认)

约定
HTTP POST .../message 同步阻塞等待 AI,最长 60s;读超时 ≥65s(§3.5 #2
响应体 成功时返回 userMessage + aiMessage;前端发送后即可展示提问,等待态文案「AI 正在思考…」
推送 AI 消息落库后 ≤1s/topic/consult/ai/{userId} 推送(与 HTTP 响应双通道,以先到者刷新为准)
非流式 不做 SSE/逐字;整段 content 一次入库(§3.5 #6、功能需求 §5.4 #10
免责声明 disclaimerPOST /sessionGET /session/list 返回,前端必须展示(§3.5 #8

6. 移动端接口

Base Path/app/consult/ai
鉴权:登录 Token;要求绑定医疗资源(牧民可用)。

# 说明 Method URI 要点
M1 新建会话 POST /session Body 可选 category14;返回 sessionIdpromptTemplatedisclaimerlast_message_time=create_time
M2 会话列表 GET /session/list asker_user_id=当前用户contentKeywordsearchMode§3.5 #5);分页;默认 3 个月
M3 历史消息 GET /session/{id}/messages 同 4.3
M4 提问 POST /session/{id}/message 同 4.4
M5 隐藏会话 POST /session/{id}/hide vet_visible=0;管理端列表同步不展示

快捷入口(§5.4 #4M1 + category → 新建会话(复用当前会话)+ 返回模板字符串,由客户端填入输入框。

模板示例(服务端常量)

category 预填模板(示例)
1 想了解一些养殖技巧,我的情况是:
2 想咨询疾病诊疗问题,症状描述:
3 想了解用药指导,具体情况:
4 想咨询设备操作问题,设备/现象:

7. 近实时、上传、菜单

与在线接诊技术方案 §5.1、§5.2 一致;订阅主题为 /topic/consult/ai/{userId}

菜单名 权限标识 路由 component
AI 诊断(兽医、机构) diseaseTreatment:aiOnlineConsult:list diseaseTreatment/onlineConsult/ai/index

按钮:querysendadd(新建会话)、remove隐藏会话)。角色 100102(机构,以平台为准)。权限 SQL:sql/biz_ai_online_consult_remove_perm.sql


8. 实现要点与类清单

说明
越权 sessionId 操作必须 consult_type=2asker_user_id=当前用户
AI 会话创建 receiver_* 全 NULL;insertsession_title=新会话
后台准入 权限注解;无医疗资源绑定校验
与接诊隔离 Mapper 方法命名区分 selectAi* / selectVet*
配置项 application.ymlconsult.ai.endpointapiKeytimeoutMs=60000(示例键名)

建议新增类

  • AiConsultValidation(搜索空条件等)
  • ConsultAiGateway / ConsultAiGatewayImpl
  • IBizAiOnlineConsultService / BizAiOnlineConsultServiceImpl
  • BizAiOnlineConsultController
  • BizAppAiConsultController
  • BizConsultSessionMapper.selectAiSessionList / insertAiSession / selectAiSessionByIdAndAsker(XML 补充)

可继续复用在线接诊已实现的

  • ConsultSessionRulesConsultRedisPushServiceBizConsultMessageMapper

9. 验收要点

编号 场景 通过标准
AI-01 新建会话 后台 POST /session 返回新 sessionId;列表出现「新会话」
AI-02 提问与回复 发送文本后 ≤60s 内返回 AI 消息;落库后 ≤1s 界面可见
AI-03 默认/搜索 同在线接诊 AC-02、AC-03(consult_type=2
AI-04 隐藏 移动端/管理端 hide 后列表无该会话
AI-05 隔离 接诊列表无 AI 会话;AI 列表无接诊会话
AI-06 机构/兽医 具备权限即可 list/create,不要求绑定医疗资源
AI-07 视频/语音 上传后 AI 回复为引导补文字文案,非解析内容
AI-08 快捷入口 点「疾病诊疗」等 → 新会话 + 预填模板
AI-09 空会话列表 POST /session 后未提问,左侧仍可见「新会话」
AI-10 处理中拒发 AI 未返回前再次发送 →「AI 正在回复中,请稍候」
AI-11 图片失败 识图失败仍有 AI 引导气泡(§3.5 #4

10. 交付清单

  • Mapper/XML:selectAiSessionList 等(consult_type=2
  • ConsultAiGateway + 配置
  • BizAiOnlineConsultServiceImpl + 后台 Controller
  • BizAppAiConsultController(M1~M5)
  • 菜单权限 SQL(§7
  • 单元测试 + MockMvc(AI 列表/新建/提问 Mock Gateway)
  • 与在线接诊联调:推送、摘要、3 个月窗一致

11. 修订记录

版本 说明
1.0 初稿:共用 bizconsult*;consult_type=2;后台/移动端接口;AI 回复流程与 60s 超时;关联在线接诊 v1.1;vet_visible 复用为列表可见
1.1 评审补充:§3.5 空会话列表、HTTP 超时、固定文案、图片软失败、移动端搜索、单轮上下文、处理中拒发、免责声明必返;§5.1 双通道;验收 AI-09~11
1.2 补充 §1.2:KbOpenAiProxyController 转发 /v1/models/v1/chat/completions;区分对话框直连/转发与 ConsultAiGateway 兜底
1.3 §1.2:/v1/** 须若依 Token,取消匿名访问
1.4 管理端 §4.5 POST /session/{id}/hide 隐藏 AI 诊断会话;权限 ...:remove
1.5 §3.1 biz_consult_session.real_session_id§3.2 biz_consult_message.ai_category(varchar32);§4.3 落库 Body/响应字段;§5 前端 aiReplyContent 落库通道
1.6 §3.0 biz_consult_user(原 biz_diagnosis_user):新建会话写入首次用户、更新 last_message_time 同步 last_use_time
1.7 表名统一为 biz_consult_user;Java BizConsultUser / BizConsultUserMapper