# 后端 API 对接指南 前端四条演示流和演示预检已经稳定。后端 API 按页面逐刀接入,避免一次性铺满四个页面。 ## 第一阶段范围 | 页面 | 后端接口 | 处理 | |---|---|---| | AI面试 `/recruit/interview` | `POST /api/recruit/interview/start`、`/answer`、`/finish` | 已接入 seed API | | 三角色对练 `/train/practice` | `POST /api/train/practice/start`、`/turn`、`/finish` | 已接入 seed API | | 案例沉淀 `/knowledge/cases` | `POST /api/knowledge/case/upload`、`/organize`、`/curate` | 已接入 seed API | | SOP知识库 `/knowledge/sop` | `POST /api/knowledge/search`、`POST /api/knowledge/doc/upload` | 已接入 MySQL Fulltext + Qdrant 混合召回、OSS-first 文档上传、txt/md/PDF/Word/Excel/PPT 解析和 embedding 写入,失败回退 seed | | 资料处理 `/knowledge/processing` | `GET /api/knowledge/processing/overview`、`POST /api/knowledge/doc/import-local-task`、`GET /api/knowledge/doc/import-tasks`、复用 `POST /api/knowledge/doc/upload` | 已接入解析任务状态聚合;页面支持多文件/目录选择、服务端后台目录导入和进度轮询,失败回退 seed | ## 后端落点 - 业务模块:`backend/ruoyi-modules/ruoyi-aihr` - 注册模块:`backend/ruoyi-modules/pom.xml` - 接入启动包:`backend/ruoyi-admin/pom.xml` - 包名建议:`org.dromara.aihr` - Controller 返回统一用 `org.dromara.common.core.domain.R` 当前四条页面流仍保留 seed fallback。知识库、模型能力、文档解析、RAG、chat 按 [ruoyi-ai 能力分片迁移计划](RUOYI_AI_INCREMENTAL_MIGRATION.md) 逐片引入;知识库 DDL 与住宅类 SOP seed 在 `backend/script/sql/aihr_knowledge_mysql8.sql`,模型 DDL 在 `backend/script/sql/aihr_model_mysql8.sql`。 直接打后端 `/api/**` 需要登录后的 `Authorization: Bearer `;浏览器内通过已登录前端和 `/dev-api` 代理访问。 SOP 文档上传第三片已经落最小后端边界: | 能力 | 后端接口 | 处理 | |---|---|---| | 文档上传解析 | `POST /api/knowledge/doc/upload` | `multipart/form-data`,字段 `file` 和 `category`;支持 `.txt/.md/.markdown/.pdf/.doc/.docx/.xls/.xlsx/.ppt/.pptx`、100MB 内;先写 `sys_oss`,再绑定 `aihr_knowledge_attach.oss_id` 并切分写入 `aihr_knowledge_fragment` | | 智能归类与标签 | 同一上传/导入链路 | `category=__auto__` 时,解析正文后优先调用已启用的 `aihr_model_config.category='chat'` 模型生成分类、摘要、标签和归类理由,温度固定为 `0`;无模型或调用失败时按文件名/正文关键词兜底。最终分类写入 `aihr_knowledge_info/attach`,摘要和标签写入 `sys_oss.ext1` | | 重复资料处理 | 同一上传/导入链路 | 上传时计算原始文件 `aihrFileSha256`、解析文本 `aihrTextSha256` 和 `md5` 写入 `sys_oss.ext1`;重复判断优先按文件 SHA-256,其次按文本 SHA-256,最后用同名同大小兼容旧数据。命中重复时复用原附件并迁移到最新分类,删除其他重复附件和旧 fragment | | 归类稳定性 | 同一上传/导入链路 | 命中重复资料时优先复用已有 `sys_oss.ext1` 里的分类、摘要和标签;只有旧资料没有存过模型结果时才重新分析,避免同文件因重复上传或更换模型导致标签漂移 | | 文档替换 | 同一 `category + fileName` 再上传 | 复用原 `doc_id`,删除旧 fragment 后重写新 fragment,避免重复文档堆积 | | 片段向量化 | 同一上传接口内机会性执行 | 若 `aihr_model_config.category='vector'` 且启用,会调用配置供应商的 OpenAI-compatible `/embeddings`,把返回向量写入 `aihr_knowledge_fragment.embedding_json` | | Qdrant 向量索引 | 同一上传接口内机会性执行 | embedding 写入 MySQL 后尽力 upsert 到 Qdrant;同名文档替换会尽力删除旧 points;Qdrant 不可用不影响上传和 MySQL 检索 | | 混合检索 | `POST /api/knowledge/search` | 先跑 MySQL Fulltext,同时在 vector 模型和 Qdrant 可用时生成 query embedding 走 Qdrant,再按 RRF 融合并回 MySQL hydrate 片段 | | 解析状态聚合 | `GET /api/knowledge/processing/overview` | 聚合 `aihr_knowledge_attach.status`、fragment 数、embedding 数、`sys_oss.ext1.fileSize`,生成资料处理页指标、分类、任务、链路和最近事件 | | 服务端目录导入 | `POST /api/knowledge/doc/import-local` | JSON `{ directory, category, limit }`;`directory` 只能是 `AIHR_IMPORT_ROOT` / `aihr.import.root` 下的相对目录,默认根目录为 `./.data/import`;逐文件复用上传解析链路,同步执行,保留给小批量/调试 | | 服务端导入任务 | `POST /api/knowledge/doc/import-local-task`、`GET /api/knowledge/doc/import-tasks` | 启动后台目录导入并返回任务;任务写入 `aihr_knowledge_import_task`,页面轮询查看总数、成功数、失败数、当前文件和进度;重试当前按同目录重新启动一轮 | 模型能力第二片已经落最小后端边界: | 能力 | 后端接口 | 处理 | |---|---|---| | 模型供应商 | `GET /api/aihr/model/providers`、`POST /api/aihr/model/providers`、`PUT /api/aihr/model/providers/{providerCode}`、`PATCH /api/aihr/model/providers/{providerCode}/status` | 优先返回 `aihr_model_provider`,支持新增、编辑、启停;缺表或空表时返回 seed 清单 | | 模型配置 | `GET /api/aihr/model/configs`、`POST /api/aihr/model/configs`、`PUT /api/aihr/model/configs/{id}`、`PATCH /api/aihr/model/configs/{id}/enabled` | 优先返回 `aihr_model_config`,支持新增、编辑、启停,并计算是否已具备 URL/Key | | 模型探针 | `POST /api/aihr/model/chat` | 数据库配置后走 OpenAI-compatible `/chat/completions`,否则 seed fallback | 本阶段不修改 `.env`,也不自动执行模型 SQL。API Key 通过模型配置页面写入数据库:`api_key` 可放在 `aihr_model_provider` 作为供应商默认值,也可放在 `aihr_model_config` 覆盖单个模型;接口响应只返回 `configured/apiKeyConfigured`,不返回密钥明文。 Qdrant 本地默认值可不配;需要覆盖时用 JVM property 或环境变量: | 配置 | 默认值 | 用途 | |---|---|---| | `AIHR_QDRANT_URL` / `-Daihr.qdrant.url` | `http://127.0.0.1:6333` | Qdrant REST 地址 | | `AIHR_QDRANT_COLLECTION` / `-Daihr.qdrant.collection` | `aihr_knowledge` | 知识库向量 collection | | `AIHR_QDRANT_API_KEY` / `-Daihr.qdrant.apiKey` | 空 | 远端 Qdrant API Key,本地不用 | | `AIHR_IMPORT_ROOT` / `-Daihr.import.root` | `./.data/import` | 服务端资料目录导入根目录 | ## 前端落点 - AI 面试 API 文件:`frontend/src/api/aihr/interview.ts` - AI 面试页面:`frontend/src/views/recruit/interview.vue` - 三角色对练 API 文件:`frontend/src/api/aihr/practice.ts` - 三角色对练页面:`frontend/src/views/train/practice.vue` - 案例沉淀 API 文件:`frontend/src/api/aihr/case.ts` - 案例沉淀页面:`frontend/src/views/knowledge/cases.vue` - SOP 知识库 API 文件:`frontend/src/api/aihr/sop.ts` - SOP 知识库页面:`frontend/src/views/knowledge/sop.vue` - 资料处理 API 文件:`frontend/src/api/aihr/processing.ts` - 资料处理页面:`frontend/src/views/knowledge/processing.vue` - 模型配置 API 文件:`frontend/src/api/aihr/model.ts` - 模型配置页面:`frontend/src/views/system/model/index.vue` - 保留本地 seed fallback:接口失败时仍能演示,不让现场 Demo 被后端状态拖死。 ## 验收 ```bash ./scripts/demo-check.sh npm --prefix frontend run lint:eslint -- src/api/aihr/interview.ts src/views/recruit/interview.vue src/api/aihr/practice.ts src/views/train/practice.vue src/api/aihr/case.ts src/views/knowledge/cases.vue src/api/aihr/sop.ts src/views/knowledge/sop.vue mvn -f backend/pom.xml -pl ruoyi-admin -am -DskipTests package ``` 模型能力可单独 smoke: ```bash TOKEN=<登录后 access_token> curl -fsS http://127.0.0.1:8080/api/aihr/model/providers -H "Authorization: Bearer $TOKEN" curl -fsS http://127.0.0.1:8080/api/aihr/model/configs -H "Authorization: Bearer $TOKEN" curl -fsS -X POST http://127.0.0.1:8080/api/aihr/model/chat \ -H "Authorization: Bearer $TOKEN" \ -H 'Content-Type: application/json' \ -d '{"prompt":"物业管家处理漏水投诉第一步是什么?"}' ``` Qdrant 可单独 smoke: ```bash curl -fsS http://127.0.0.1:6333/ ``` 浏览器验收仍按 [DEMO_ACCEPTANCE.md](DEMO_ACCEPTANCE.md) 的四条关键路径执行。