# AI 诊断（兽医、机构）— 前端技术方案

> 依据：`AI诊断(兽医、机构)功能需求.md`、`AI诊断(兽医、机构)技术方案.md`（**v1.5**）；实现文件 `ruoyi-ui/src/views/diseaseTreatment/onlineConsult/ai/index.vue`；布局参考同目录在线接诊（左右分栏 IM）。

---

## 1. 页面与路由

| 项 | 说明 |
| --- | --- |
| **Vue 路径** | `ruoyi-ui/src/views/diseaseTreatment/onlineConsult/ai/index.vue` |
| **组件名** | 以若依菜单 `component` 配置为准 |
| **菜单配置（示例）** | 组件路径：`diseaseTreatment/onlineConsult/ai/index` |
| **权限前缀** | `diseaseTreatment:aiOnlineConsult:list`、`query`、`add`（发送走大模型直连 + 落库，**未使用** `send` 按钮权限） |

**准入**：列表/新建/历史消息仅需登录与 `diseaseTreatment:aiOnlineConsult:*` 权限；后端**不校验**是否绑定兽医/机构医疗资源。

---

## 2. 接口分工（双通道）

本期前端采用 **会话管理走后端、对话框问答走大模型网关** 的混合方案。

| 能力 | 调用方式 | 说明 |
| --- | --- | --- |
| 会话列表 | 若依 `request` → `GET /diseaseTreatment/onlineConsult/ai/session/list` | 含 `disclaimer` |
| 新建会话 | `POST .../session` | 返回 `id`/`sessionId`、`disclaimer` |
| 历史消息 | `GET .../session/{id}/messages` | 打开会话、上拉加载更早 |
| **提问与 AI 回复** | **`streamChatMessage`**（`utils/llmStreamChat.js`）→ `{VUE_APP_LLM_BASE_URL}/v1/chat/completions` | 对话框问答，支持 SSE 流式；失败可回退非流式 |
| **会话落库** | `POST .../session/{id}/message` | Body：`msgType`、`content`、`aiReplyContent`、`realSessionId?`、`aiCategory?`；大模型返回后落库，**不**再调后端 AI 网关 |
| 媒体上传 | `POST /common/upload` | 先上传再随提问送 LLM |

> **与后端技术方案**：后端 **§4.4** 同时支持全后端 `ConsultAiGateway` 通道与前端 `aiReplyContent` 落库通道。当前页面采用后者。  
> **大模型上下文**：`session.llmSessionId` / `realSessionId` 存网关返回的 `session_id`，与后端会话主键 `activeSessionId` 分离；落库时通过 `realSessionId` 写入 `biz_consult_session.real_session_id`。

---

## 3. API 模块（若依）

| 项 | 说明 |
| --- | --- |
| **文件** | `ruoyi-ui/src/api/diseaseTreatment/aiOnlineConsult.js` |
| **Base** | `/diseaseTreatment/onlineConsult/ai` |

| 方法 | HTTP | 页面是否使用 |
| --- | --- | --- |
| `listAiConsultSessions` | GET `/session/list` | ✓ |
| `createAiConsultSession` | POST `/session` | ✓ |
| `listAiConsultMessages` | GET `/session/{sessionId}/messages` | ✓ |
| `sendAiConsultMessage` | POST `/session/{sessionId}/message`（timeout 65s，`repeatSubmit: false`）；Body 含 `msgType`、`content`、`aiReplyContent`、`realSessionId?`、`aiCategory?` | ✓（落库） |

**列表 Query（小驼峰）**：`pageNum`（默认 1）、`pageSize`（默认 100）、`contentKeyword`、`searchMode`（仅点击搜索为 `true`）。

**消息 Query**：`beforeId`、`pageSize`（默认 50）。

---

## 4. 大模型对话（对话框）

| 项 | 说明 |
| --- | --- |
| **环境变量** | `VUE_APP_LLM_BASE_URL`（开发常为 `/llm-dev-proxy`，由 `vue.config.js` 转发）、`VUE_APP_LLM_API_KEY` |
| **请求** | `POST {llmBase}/v1/chat/completions` |
| **鉴权（直连上游）** | `Authorization: Bearer {VUE_APP_LLM_API_KEY}` |
| **鉴权（经 8010 转发）** | **`Authorization: Bearer {getToken()}`**（若依登录 Token，与 `request.js` 一致）；**不能**仅用 `VUE_APP_LLM_API_KEY` |
| **超时** | 120000 ms |
| **Body** | `model`、`messages[]`、`stream: true`（优先流式）、`user`；可选 `session_id`（`llmSessionId`） |
| **流式解析** | `utils/sseParse.js`、`utils/llmResponseParse.js`（提取 `session_id`、增量 `content`） |
| **后端转发（可选）** | `VUE_APP_LLM_BASE_URL` → `http://localhost:8010`；`KbOpenAiProxyController` 须登录，详见 `doc/大模型/大模型网关转发接口说明.md` §3、§6 |

**模型选项（前端常量）**

| value | 说明 |
| --- | --- |
| `auto` | 自动选择 |
| `yak-disease` | 疫病诊疗助手 |
| `yak-general` | 通用助手 |
| `yak-feeding` | 饲喂决策助手 |

**`messages` 构建**：将当前会话 `messages` 转为 OpenAI 风格；`senderRole === 1` → `user`，`senderRole === 3` → `assistant`；`msgType === 2` 图片转为 `image_url`。

---

## 5. 页面结构

1. **筛选区**：会话内容关键字；**搜索**（须非空，`searchMode=true`）、**重置**（默认近 3 个月）、**新增会话**。  
2. **免责声明**：`disclaimer` 自列表/新建响应写入，工具栏下 `el-alert` **常驻展示**。  
3. **左侧历史会话**：`sessionTitle`、摘要、`lastMessageTime`（格式 **M月D日**）；**本期不改前端**：隐藏会话后端已提供 `POST .../session/{id}/hide`（§4.5），页面未接按钮/API 封装。  
4. **右侧对话区**：标题为当前会话标题；消息按日分组；用户气泡在右（绿）、AI 在左（蓝）。  
5. **输入区**：文本、图片/视频/语音上传、模型选择、发送；等待时展示「AI 正在思考…」动效。

---

## 6. 业务规则（前端）

| 规则 | 实现 |
| --- | --- |
| 默认列表 | 不传 `searchMode`；近 3 个月有消息的会话 |
| 搜索 | `contentKeyword` 非空 + `searchMode=true`；空关键字提示「请输入会话内容后再搜索」 |
| 会话主键 | `resolveSessionId(s)`：`id` 优先，否则 `sessionId` |
| 消息角色 | `senderRole`：`1` 提问人（右）、`3` AI（左） |
| 消息类型 | `msgType`：`1` 文本、`2` 图片、`3` 视频、`4` 语音 |
| 发送流程 | 先本地展示提问 → `streamChatMessage` 调 LLM → 本地展示 AI 回复 → `sendAiConsultMessage`（`aiReplyContent`、`realSessionId`、`aiCategory`）落库并合并服务端消息 ID |
| 思考中 | `sending` 且最后一条为 `senderRole === 1` 时显示占位气泡 |
| 上拉历史 | `scrollTop < 40` 时用 `beforeId` 加载更早 50 条 |
| 轮询 | 选中会话后 **2s** 仅 `refreshSessionListQuiet`（**不**轮询消息，避免覆盖仅存在本地的 LLM 对话） |
| 媒体限制 | 图 jpg/jpeg/png/gif ≤10MB；视频 mp4/mov ≤50MB；语音 mp3/m4a/wav ≤10MB；文件名不含英文逗号 |
| 本地消息 ID | 前缀 `m_`（`genLocalId`），与服务端数字 ID 区分 |

**数据持久化说明**：大模型返回后调用 `POST .../message` 并传 `aiReplyContent`、可选 `realSessionId`（大模型 `session_id`）、`aiCategory`（提问分类）；服务端仅落库、不调 `ConsultAiGateway`；响应 `data.realSessionId` 可与本地 `llmSessionId` 对齐。落库失败时界面仍保留本地气泡并提示「保存会话失败」。

---

## 7. 与在线接诊（兽医）差异

| 项 | AI 诊断 | 在线接诊（兽医） |
| --- | --- | --- |
| 列表标题 | `sessionTitle` | `askerName` |
| 筛选 | 仅 `contentKeyword` | 问诊人 + 会话内容 |
| 新建会话 | 有 | 无 |
| 对话对象 | AI（`senderRole=3`） | 问诊人 / 兽医（1/2） |
| 发送接口 | 大模型 `chat/completions` | `POST .../vet/session/{id}/message` |
| 消息轮询 | 仅刷新会话列表 | 消息 + 列表 |

---

## 8. 国际化

| 项 | 说明 |
| --- | --- |
| **语言包** | `diseaseTreatment.aiOnlineConsult`（`lang/zh/diseaseTreatment.js`、`lang/bo/diseaseTreatment.js`） |
| **Mixin** | `diseaseTreatmentLocaleMixin`，页面 `dtNs = 'aiOnlineConsult'`，`dtT` / `dtCommon` |

---

## 9. 文件清单

| 路径 | 说明 |
| --- | --- |
| `src/views/diseaseTreatment/onlineConsult/ai/index.vue` | 主页面（IM + LLM 流式发送 + 落库） |
| `src/api/diseaseTreatment/aiOnlineConsult.js` | 若依会话/消息 API |
| `src/utils/llmStreamChat.js`、`sseParse.js`、`llmResponseParse.js` | 大模型流式与响应解析 |
| `src/mixins/diseaseTreatmentLocaleMixin.js` | 文案 |
| `src/lang/zh/diseaseTreatment.js` | 中文 `aiOnlineConsult` |
| `src/lang/bo/diseaseTreatment.js` | 藏文 `aiOnlineConsult` |
| `.env.development` / `.env.production` | `VUE_APP_LLM_*` |
| `vue.config.js` | 开发环境 `LLM_PROXY_TARGET` → `/llm-dev-proxy` |

---

## 10. 联调说明

1. 登录账号须具备 AI 诊断菜单与接口权限；**不要求**在「畜牧医疗资源管理」绑定兽医/机构资源。  
2. 配置 `VUE_APP_LLM_BASE_URL`、`VUE_APP_LLM_API_KEY` 后重启 `npm run dev`。  
3. 新建会话后左侧应出现「新会话」；发送后 Network 应先后出现 **`/v1/chat/completions`** 与 **`/session/.../message`**（`aiReplyContent` 落库）。  
4. 与在线接诊数据隔离：`consult_type=2` 会话不出现在接诊列表。  
5. **切换全后端 AI**：将 `sendViaLlm` 改为调用 `sendAiConsultMessage`，合并 `res.data.userMessage` / `aiMessage`，并恢复消息轮询或 STOMP（见后端 **§5.1**）。

---

## 11. 修订记录

| 版本 | 说明 |
| --- | --- |
| 1.0 | 初稿：混合架构（会话后端 + 对话 LLM 直连）；对齐 `onlineConsult/ai/index.vue` 实现 |
| 1.1 | 落库通道：`sendAiConsultMessage` + `aiReplyContent`；`realSessionId`、`aiCategory`；LLM SSE 流式 |
