# 后端 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 | | 移动端手机号登录 | `GET /resource/sms/code`、`POST /auth/mobile/sms-login` | 已复用 sms4j 阿里云配置 `config1` 和 RuoYi `sms` 授权策略;短信发送成功后才写 Redis 验证码;手机号不存在时自动注册 `app_user` | | 移动端三端首页 `/h5/user`、`/h5/candidate`、`/h5/supervisor` | `GET /api/aihr/mobile/home/{role}` | 已接入员工、候选人、主管首页 seed API;移动端本地 fallback 保演示 | | 移动端员工训练闭环 | 复用 `POST /api/train/practice/start`、`/turn`、`/finish`;查询 `GET /api/aihr/mobile/practice/history`、`/practice/reviews`、`/practice/reviews/{id}`、`/profile`;标记 `POST /api/aihr/mobile/practice/reviews/{id}/reviewed` | 员工端登录后带 `Authorization` 与 `clientid` 调用;`mode=mobile` 完成后写入 `aihr_practice_session`,主管端首页完训率、待复盘列表、复盘详情、员工训练历史和能力画像同步变化 | ## 后端落点 - 业务模块:`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`,训练记录 DDL 在 `backend/script/sql/aihr_practice_mysql8.sql`。 直接打后端 `/api/**` 通常需要登录后的 `Authorization: Bearer `;浏览器内通过已登录前端和 `/dev-api` 代理访问。移动端登录接口为 `POST /auth/mobile/sms-login`,请求 `{ phonenumber, smsCode, tenantId }`,内部固定使用 app 客户端 `428a8310cd442757ae699df5d894f051` 和 `sms` grant;验证码通过后若手机号不存在,会创建 `app_user`,用户名为手机号,备注为“移动端短信自动注册”。移动端 MVP 首页接口 `GET /api/aihr/mobile/home/{role}` 目前仍是 `@SaIgnore` 的公开只读 seed 接口,避免 H5 首屏被后台管理登录态阻断;后续接小程序登录后再收紧为移动端 token。 阿里云短信复用 RuoYi 的 `sms.blends.config1`。本地开发把真实短信参数放根目录 `.env.local` 或外部环境变量,`scripts/dev-backend.sh` 会自动加载;`application-dev.yml` / `application-prod.yml` 只保留占位,不写真实密钥。 ```bash AIHR_SMS_LOGIN_TEMPLATE_ID=SMS_xxxxxx ALIYUN_SMS_ACCESS_KEY_ID=xxx ALIYUN_SMS_ACCESS_KEY_SECRET=xxx ALIYUN_SMS_SIGN_NAME=物业AI助手 ``` SOP 文档上传第三片已经落最小后端边界: | 能力 | 后端接口 | 处理 | |---|---|---| | 文档上传解析 | `POST /api/knowledge/doc/upload` | `multipart/form-data`,字段 `file` 和 `category`;支持 `.txt/.md/.markdown/.pdf/.doc/.docx/.xls/.xlsx/.ppt/.pptx` 及图片 `.jpg/.jpeg/.png/.gif/.webp/.bmp`、100MB 内;先写 `sys_oss`,再绑定 `aihr_knowledge_attach.oss_id` 并切分写入 `aihr_knowledge_fragment` | | 图片视觉 OCR | 同一上传/导入链路 | 上传图片时,若启用了 `aihr_model_config.category='vision'`(无则回退 `category='chat'`)的模型,会把图片编码为 base64 data URL 调用该供应商 OpenAI-compatible `/chat/completions` 提取文字,识别结果按普通正文切分写入 fragment;无视觉模型时不报错,返回空正文、fragment 为 0。不依赖 Tesseract,识别质量由所配置视觉模型决定 | | 智能归类与标签 | 同一上传/导入链路 | `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`;配置缺失或外部接口失败时使用本地确定性 `local-hash-v1` 兜底,保证 MVP 链路仍有真实向量数据 | | 缺失向量补跑 | `POST /api/knowledge/doc/vectorize-missing` | 扫描缺少 `embedding_json` 的片段,复用同一向量化与 Qdrant 入库链路,适合模型配置变更后或历史资料补齐向量 | | 向量库状态与重建 | `GET /api/knowledge/doc/vector-index-status`、`POST /api/knowledge/doc/rebuild-vector-index` | 模型配置页展示当前 vector 模型维度、Qdrant collection 维度、点数和片段向量数;维度不一致时可一键清空旧 embedding、删除 collection 并按当前模型重建 | | Qdrant 向量索引 | 同一上传接口内机会性执行 | embedding 写入 MySQL 后尽力 upsert 到 Qdrant;同名文档替换会尽力删除旧 points;Qdrant 不可用不影响上传和 MySQL 检索;外部 embedding 成功但 collection 维度不一致时不静默降级为 `local-hash-v1`,通过状态接口和重建入口处理 | | 混合检索 | `POST /api/knowledge/search` | 先跑中文关键词 `LIKE` 打分和 MySQL Fulltext,再生成 query embedding 走 Qdrant,最后按 RRF 融合并回 MySQL hydrate 片段;Qdrant 或外部向量接口不可用时保留关键词/全文检索 | | 解析状态聚合 | `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` | 服务端资料目录导入根目录 | ## 前端落点 - 管理端前端:`frontend/`,只承载后台管理、配置、审核、查看结果等页面。 - 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` - 移动端独立工程:`mobile/src/App.vue`,路由 `/h5/user`、`/h5/candidate`、`/h5/supervisor`;当前三端首页已按高保真原型实现,未登录先走手机号短信登录,登录后 API 优先请求 `/api/aihr/mobile/home/{role}`,不复用后台页面路由。 - 员工端训练:任务卡“开始训练”复用管理端三角色对练 seed API,完成两轮后展示评分和导师改写;记录写入 `aihr_practice_session`,员工端展示训练历史和能力画像,主管端展示待复盘列表,点进单条可看评分、话术、导师改写,并可标记“已复盘”。 - 保留本地 seed fallback:接口失败时仍能演示,不让现场演示被后端状态拖死。 ## 验收 ```bash ./scripts/demo-check.sh npm --prefix mobile run build curl -fsS http://127.0.0.1:8080/api/aihr/mobile/home/user 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;重建接口会清空旧 embedding 并重建 collection,只用于本地验证或确认维度不一致后的修复: ```bash TOKEN=<登录后 access_token> curl -fsS http://127.0.0.1:6333/ curl -fsS http://127.0.0.1:8080/api/knowledge/doc/vector-index-status -H "Authorization: Bearer $TOKEN" curl -fsS -X POST http://127.0.0.1:8080/api/knowledge/doc/rebuild-vector-index -H "Authorization: Bearer $TOKEN" ``` 浏览器验收仍按 [DEMO_ACCEPTANCE.md](DEMO_ACCEPTANCE.md) 的四条关键路径执行。