Files
prop-ai-hr/docs/API_INTEGRATION.md
T

422 lines
78 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 后端 API 对接指南
本文维护当前前后端业务接口和安全边界;旧演示流继续保留降级链,但不能作为生产或试点通过证据。
## 第一阶段范围
| 页面 | 后端接口 | 处理 |
|---|---|---|
| AI面试 `/recruit/interview` | `POST /api/recruit/interview/start`、`/answer`、`/finish`、`GET /records`、`POST /records/{sessionId}/review` | 已接入真实模型优先链路:配置 chat 模型后 `/start` 动态生成面试题,`/finish` 按前端提交的真实回答做结构化评分;未配置模型时使用本地 Rubric 估分,不返回固定候选人分数。AI 分数只作辅助参考,HR/管理员可提交 `reviewedScore/reviewNote` 完成人工复核;记录同时保留 AI 分、人工复核分、最终采用分和复核状态 |
| 候选人入职主体关联 `/recruit/interview` | `GET/POST /api/recruit/interview/candidate-links` | HR/管理员在当前租户范围内把本地候选人 ID 关联到已同步的在职 `ext_party_id`;只保存外部主体 ID,不复制姓名、部门等组织字段,重复关联同一主体幂等,候选人更换主体或同一主体已关联其他候选人会拒绝 |
| 三角色对练 `/train/practice` | `POST /api/train/practice/start`、`/turn`、`/finish` | 已接入编排 API;数据库启用 chat 模型后,`/turn` 客户回复按人设走真 LLM 生成(seed 剧本作剧情锚点),`suggestReply=true` 时服务端按当前会话、回合和老师傅提示生成一条可编辑但不自动发送的员工回复,失败明确提示且不复制指导语;`/finish` 走单次 temperature=0 结构化评分(五维分、导师改写、点评)。APP 员工请求由服务端绑定当前登录手机号并强制 `mode=mobile`,不信任客户端的身份或模式;`turn/finish` 只允许该员工操作本人活动/已完成会话。`superadmin` 或 `hr_operator` 的管理端预览可复用同一路径,但服务端固定为 `operator:{userId}` 与 `mode=preview`,忽略客户端 `extPartyId/mode/assignmentId`;预览记录不进入员工历史、主管复盘、成长、试点或正式校准统计。其他后台身份拒绝。已完成会话的重复 `/finish` 只读取持久化结果,绝不按当前场景目录重算或回写历史快照;正式训练回合模型未配置或调用失败自动回退 seed,契约不变。 |
| 训练场景运营 `/train/scenarios` | 内容读取 `GET /api/train/practice/scenarios`、`GET /scenarios/{id}`;管理写入 `POST /scenarios`、`PATCH /scenarios/{id}/enabled`、`PATCH /scenarios/{id}/review-status`;目录 `GET /curriculum/capabilities`、`GET /curriculum/matrix` | 读取默认只返回当前租户“已发布且启用”的场景,且不返回审核人、稳定用户 ID 或审核时间;仅 `superadmin/hr_operator` 可请求 `includeDisabled=true`、编辑、启停和推进审核。状态只能按 `草稿 → 待业务审核 → 已发布 → 已下线` 流转;发布时服务端重新读取场景并校验住宅范围、固定五岗位、成长能力项、业务来源/SOP、服务红线、目标、成功标准、至少两轮回合和完整五维 Rubric,不信任客户端传入的发布内容。每次编辑已发布内容会退回待业务审核并刷新内容版本/哈希及审核留痕。运营可以选择风险级别,但服务端会对收费、安全、投诉等受控词及全部回合文本自动升级为“高风险”;待评估内容不可发布。高风险场景先记录第一位审核人的稳定用户 ID 和显示名并保持待业务审核,只有第二位不同的已认证运营人员复核同一内容版本后才发布;迁移重跑不得清空等待复核的首审记录。员工可见和可启动路径始终同时要求 `review_status=已发布 AND enabled=1`。 |
| M2 训练预习与求助 | `GET /api/train/practice/scenarios/{id}/prep-card`、`POST /api/train/practice/sessions/{sessionId}/help` | 均要求已登录;预习卡只使用当前租户已启用的 `prep_card/json_prep` 模板,模型返回必须严格为 3 条要点、3 条红线、2 条话术,否则回退场景卡。求助接口只允许 APP 员工访问其本人、同租户的活动训练会话;客户端可传 `roundIndex` 仅为兼容字段,服务端按会话实际进度落库,返回 `{sessionId,scenarioId,roundIndex,recordedAt}`。迁移表为 `aihr_practice_help_event`,不在请求路径执行 DDL。该表已随 2026-07-24 完整包发布并纳入远端 `62/62` 结构预检;完整训练写入与正式试点仍需另验。 |
| 正式试点数据导出 | `GET /api/train/practice/export?startDate=YYYY-MM-DD&endDate=YYYY-MM-DD` | 起止日期必填且包含结束日;只统计窗口内能通过唯一手机号或外部 ID 映射到在职组织快照的正式会话,排除重复手机号和身份碰撞。完训定义为每人至少 10 次,校准必须关联同一窗口内正式会话;CSV 同时给出校准命中数、SOP 可用数、满意度响应数/平均分,以及明细级 AI 分、人工校准分、校准人、校准时间、最终采用分、满意度分和意见,避免用四舍五入后的比率反推门禁状态;汇总和明细均携带正式人员及项目口径,不混入历史 seed/开发身份 |
| 对练语音 | `POST /api/ai/asr`(multipart 字段 `file`,≤5MB)、`POST /api/ai/tts`(JSON `{text≤300字, voice?, voiceProfile?:{role,voice?,speed?,emotion?,dialect?}, practiceContext?:{sessionId,turnIndex,role:'customer'}}`,`dialect` 仅接受 `mandarin/cantonese/sichuanese`,非法值返回业务码 `400`;旧 `voice` 兼容;成功返回 `ossId`,客户端播放地址为受控的内联 `data:audio/*`,不返回原始 OSS URL)、`GET /api/aihr/mobile/oss/{ossId}` | ASR 按供应商分流:SiliconFlow 等 OpenAI audio 兼容服务继续走 `/audio/transcriptions` multipart;阿里云百炼 `dashscope/qianwen + qwen3-asr-flash` 走工作空间 `/compatible-mode/v1/chat/completions`,将短音频编码为 Base64 `input_audio` 并读取 `choices[0].message.content`,请求上限仍由本接口收窄为 5MB。生产 TTS 优先阿里 `qwen-audio-3.0-tts-flash`,旧 SiliconFlow CosyVoice2 保留兼容;按角色映射老师傅/业主/面试官音色,业主对练再按已有情绪分切换平静/严肃/强烈语气和语速;dialect 作为语气提示传给支持表现力提示词的 TTS,不含克隆,也不等同于方言正式验收。`practiceContext` 仅允许已认证 APP 员工把本次服务端生成的业主 TTS 绑定到本人当前或刚完成会话的指定原话术回合;服务端重新校验租户、员工身份、回合和文本,不接受员工端传 OSS ID 或任意角色。设备语音降级按方言选择语言:粤语使用 `zh-HK`,四川话无法由设备语音可靠模拟时明确提示用户转用服务端语音或文字,不伪装成普通话。模型管理需启用 `category=asr/tts` 配置;移动端优先用浏览器录音,`getUserMedia/MediaRecorder` 不可用或麦克风权限失败时,用 `audio/*` file input 选择/录制音频后继续调同一 ASR 接口;ASR/TTS 未配置或失败返回 fail,前端降级设备语音或文本;训练/每日题录音只通过 `audioOssId` 走受保护下载,历史客户端传入的 HTTP `audioUrl` 不再回显;TTS 成功音频仍写入 `sys_oss` 留痕,同时用内联 data URL 保持旧客户端可播放;移动端 OSS 下载只允许经本人或主管项目范围验证的 APP 身份,后台账号若需回放必须使用未来独立、受审计的管理端契约。 |
| 实时对练 Beta(WebRTC) | `POST /api/train/practice/realtime/sdp`、`GET /realtime/personas`、`POST /realtime/session`、`POST /realtime/tools/invoke` | H5 浏览器及 App-Plus renderjs WebView 内的 WebRTC 直连阿里云(音频不过服务端),后端仅代理 SDP 交换(3 次/分/账号,sessionId 仅作日志关联且限字符)。人设为服务端 `AihrRealtimePersonaRegistry` 白名单:`owner-calm` 业主·常规(默认)、`owner-impatient` 业主·急躁、`digital-mentor` 数字师傅;人设绑定默认音色,客户端只选不编。`session.update`(instructions/音色/turn_detection/tools 声明)由 `/realtime/session` 组装下发,客户端原样转发;personaId 空白回落默认人设;会话记录写 Redis 30min(成功工具调用滑动续期),创建限流 6 次/分/账号。`digital-mentor` 声明 `search_knowledge` 工具:模型 function calling 命中(`response.function_call_arguments.done`)时客户端回传 `/realtime/tools/invoke`,服务端按登录态 + Redis 会话归属 + 人设工具白名单三重校验后走 `searchAuthorized` 浅层检索(不生成 LLM 答案、不落 `aihr_agent_run`;`source=realtime` 跳过 SOP 评审与知识缺口写入),output 拼接 ≤3 条片段并截断 1200 字;工具按账号限流 20 次/分,无授权/无结果/超时一律返回软着陆文案让模型如实作答,不中断语音会话。实时 Beta 不计分、不持久化训练数据;H5 与 App-Plus 均支持,App-Plus 在 renderjs 视图层执行媒体/WebRTC 并通过逻辑层复用受认证 SDP 与工具接口;需 APP 员工登录态,不启用模型 `enable_search`。配置 `AIHR_QWEN_REALTIME_ENDPOINT` / `AIHR_QWEN_REALTIME_API_KEY` / `AIHR_QWEN_REALTIME_MODEL`(模型白名单 `qwen3.5-omni-(flash|plus)-realtime`),密钥只放 `.env.local` 或外部环境变量。 |
| 案例沉淀 `/knowledge/cases` | `GET /api/knowledge/case/capabilities`、`POST /api/knowledge/case/upload`、`/organize`、`/curate`、`GET /records`、`GET /records/{caseId}`、`POST /records/{caseId}/review`、`POST /records/{caseId}/publish`、`POST /records/{caseId}/retire`、`GET /records/{caseId}/media` | `/capabilities` 返回服务端判定的案例提交/查看能力,移动端不再向普通员工展示无权提交的素材表单;`/upload` 改为 multipart 真实语音上传并走 ASR,服务端只接受 MP3/WAV/M4A/WebM/OGG/AAC/FLAC,成功后原始音频写入 `sys_oss`,案例记录只保存 `mediaOssId` 供受保护媒体接口读取,不向客户端回传原始 `mediaUrl`;`/organize` 用真实转写调 chat 模型整理案例,未配置模型时按真实 transcript 本地结构化,并从背景外的真实摘要项提取学习点;`/curate` 只进入经验候选池,`/review` 只完成业务审核,`/publish` 才能在来源授权、脱敏、适用岗位、负责人、版本和生效期齐全时进入正式经验库,`/retire` 要求填写下线原因并立即停止员工召回;APP 用户必须先以认证手机号精确校验在职 `person_phone`,再由可信主体解析 `aihr_org_snapshot` 项目范围;案例 SQL 不得把 `person_phone` 与 `ext_party_id` 作为替代查询键。上传、整理、候选、审核、发布、下线、列表和详情均按该项目范围校验,未完成正式组织映射时安全拒绝,不接受前端伪造项目范围;员工列表/详情只返回 `knowledge_status=PUBLISHED` 且处于生效期的 `已入库` 案例,管理端系统用户保留全局运营视图;移动端和管理端案例详情通过受保护媒体接口回放原始音频,主管/项目负责人可提交脱敏点评;预渲染视频样片仍待正式媒体资产接入 |
| 案例媒体安全 | `GET /api/knowledge/case/records/{caseId}/media` | 案例详情只返回 `mediaOssId`,不返回原始 `sys_oss.url`;媒体下载会重复执行登录、后台角色或 APP 项目范围校验,再由服务端流式读取 OSS。管理端与 `mobile-uni` 通过鉴权 blob/temp 文件播放,关闭详情页时释放本地对象 URL |
| 问·数字师傅工作 Agent `/pages/user/sop/index` | `POST /api/aihr/agent/messages`、`POST /api/aihr/agent/messages/media`、`POST /api/aihr/agent/actions/{draftId}/confirm`、`POST /api/aihr/agent/actions/{draftId}/dismiss` | 移动端文字、ASR 转写和图片/视频统一进入 Agent。服务端根据消息规划 `KNOWLEDGE_QA/RESOURCE_DELIVERY/LIVE_MY_WORK/LIVE_TEAM_WORK/PRACTICE_COACHING/CAPTURE_FACT/MEDIA_UNDERSTANDING/WEB_RESEARCH/CLARIFY/SOCIAL`,再由固定策略校验当前登录身份和工具权限;客户端不提交 `toolCode`,模型也不能自由指定身份、SQL、URL 或任意工具。知识、训练概况、当前待办、确认式记忆、媒体分析和全网查询均复用现有领域服务;普通图片问答只做本次视觉分析,不要求先具备知识空间,用户明确问现场制度/流程时才校验授权空间并补充 SOP。全网查询先返回 `NEEDS_INPUT`,只有用户明确同意后才调用外部服务。统一响应使用结构化 `status/sourceSummary/citations/resources/actionDraft/clarification`;按钮不从答案文本推断。写入动作只接受 30 分钟有效的不透明 `draftId`,最终确认继续复用既有版本、幂等与可见性规则。每次运行只审计租户、主体、意图、工具、状态、来源类型、耗时和错误码,不保存原始问题、答案或附件。 |
| SOP 知识底层服务 `/knowledge/sop` 与旧客户端兼容 | `POST /api/knowledge/query`、`POST /api/knowledge/query-media`、`GET /api/knowledge/citations/{detailRef}`、`POST /api/knowledge/citations/{detailRef}/media-link`、`GET /api/knowledge/resources/{attachmentId}/content`、`POST /api/knowledge/resources/{attachmentId}/download-link`、`GET /api/knowledge/resources/{attachmentId}/download?ticket=`、`POST /api/knowledge/answer-feedback`、`GET /api/knowledge/position-sop`、`GET /api/aihr/mobile/onboard/tasks`、`POST /api/aihr/mobile/onboard/tasks/{id}/complete`、`GET /api/aihr/mobile/qualification`、`POST /api/knowledge/doc/upload` | Agent 按规划结果调用该层;管理端和旧客户端接口继续兼容。文字/现场附件查询走统一知识空间授权和 MySQL Fulltext + Qdrant 混合召回;内部登录端可选传 `conversationId/contextVersion`,服务端保留 30 分钟不活跃过期、最新 6 轮脱敏截断上下文,用现有 chat 模型或保守规则输出 `QA/FILE/VIDEO/DATA_TOOL` 和 `rewrittenQuery`,旧版本冲突返回 `409`。员工从银城大喇叭详情追问时只传 `broadcastMessageId`:服务端每轮按当前 APP 在职员工、当前租户和 `PUBLISHED` 状态重新取消息,响应仅回显 `{messageId,title,publishedAt}`,消息正文不写入客户端存储、会话 JSON 或查询审计;同一会话的消息来源不可改绑,撤回、跨租户或非在职员工均拒绝。该上下文只说明公司消息,不可据此捏造个人任务;上下文模式拒绝现场媒体和数据工具,外部无状态 API 不接受该参数。岗位 SOP 的项目范围复用当前 APP 已验证主体的 `projectCodes`,不得以管理端组织快照的模糊/别名查询作为后备。原文件、图片和视频只从当前命中且仍有授权的附件返回;回答只携带不透明 `detailRef` 和最小定位摘要,详情及媒体票据接口每次重新计算租户、应用、主体和来源状态。文本详情返回受控分段和命中标记,图片/视频通过短期单附件票据以内联 MIME 响应读取,视频支持 HTTP Range;不返回原始 OSS 地址或登录令牌。无资源时明确返回空列表,不生成假链接。搜索继续返回 `reviewId/promptVersion`,员工反馈保存评审批次;员工学习页按当前 APP 身份读取正式岗前/入职任务,资格证据无正式数据时明确返回 `NOT_CONFIGURED`。详细边界见 [专项设计](20260718/问师傅多轮会话与原始资料交付设计.md)和[引用详情实施计划](superpowers/plans/2026-07-31-mobile-citation-source-viewer.md)。 |
| 工作助手确认式统一采集 `/pages/user/sop/index`、`/pages/user/assistant/memories` | Agent 主入口返回 `actionDraft`,确认/忽略走 `/api/aihr/agent/actions/{draftId}/confirm|dismiss`;旧接口 `POST /api/knowledge/query`、`POST /api/knowledge/query-media`、`GET /api/aihr/personal-assistant/memory-candidates?status=DRAFT`、`POST /api/aihr/personal-assistant/memory-candidates/{id}/confirm|dismiss` 继续兼容;确认记录读写使用 `GET /api/aihr/personal-assistant/assistant-captures?limit=50`、`POST /assistant-captures/{id}/status`、`GET /assistant-captures/{id}/status-history`、`GET /assistant-captures/{id}/source`,旧项目记录继续使用 `/api/aihr/service-memories` | 文字、ASR 转写和现场媒体复用同一候选检测。项目工作必须从 `/api/aihr/mobile/me` 返回的项目名称列表选择当前项目,内部 `projectCode` 仍由服务端按当前登录主体授权收窄;个人笔记可以不绑定项目。显式“记一下”生成 `ASSISTANT_CAPTURE/DRAFT`,系统建议 `ATTENDANCE/INSPECTION/SERVICE_LEAD/RESIDENT_PROFILE/CASE/PERSONAL_NOTE/FOLLOW_UP/PROJECT_NOTE`。确认必须带 `expectedVersion + idempotencyKey + saveScope(PRIVATE|COMPANY)`;PRIVATE 仅本人可见,COMPANY 写入 PENDING 待流转。状态更新只允许 `RECORDED/PENDING/IN_PROGRESS/COMPLETED/VOID`,幂等键不得跨记录或目标状态复用。来源文件重新鉴权交付;正式外部接收端未配置前不得显示已送达。 |
| 今日工作成果 `/pages/user/work-results/index`、`/pages/supervisor/work-results/index` | 员工 `POST /api/aihr/work-results/mine/generate?projectCode=...&workDate=YYYY-MM-DD`;主管 `GET /api/aihr/work-results/project?projectCode=...&workDate=YYYY-MM-DD` | 按员工 + 项目 + Asia/Shanghai 自然日确定性聚合已确认记录;两页默认当天并可按日期查看历史成果,客户端禁止选择未来日期。内容未变化时重复生成不增加版本,记录或状态变化后版本递增。主管只可查看本人在目标项目具备主管身份的团队结果;APP 登录身份同时兼容组织快照中的外部人员 ID 与手机号,普通员工或跨项目请求返回 403。外部接口未接通时只显示本地状态和 PENDING,不伪造派单或考勤同步成功 |
| 银城大喇叭 | 员工 `GET /api/aihr/broadcast/unread-count`、`GET /api/aihr/broadcast/messages?pageNum=&pageSize=`、`GET /api/aihr/broadcast/messages/{id}`、`POST /api/aihr/broadcast/messages/{id}/read`、`GET /api/aihr/broadcast/attachments/{id}/view`、`GET /api/aihr/broadcast/attachments/{id}/content`;员工详情可经统一 `POST /api/knowledge/query` 的 `broadcastMessageId` 发起文字追问;管理 `GET /api/aihr/broadcast/admin/messages`、`POST /api/aihr/broadcast/admin/attachments`、`GET /api/aihr/broadcast/admin/attachments/{id}`、`PUT /api/aihr/broadcast/admin/attachments/{id}/topic-tags`、`POST /api/aihr/broadcast/messages`、`POST /api/aihr/broadcast/messages/{id}/withdraw` | 发布支持 `visibilityMode=ALL/TARGET_ONLY`、必读、单个公司文件与异步提炼;`TARGET_ONLY` 必须有目标,发布时冻结匹配收件人,列表、详情、未读、阅读、附件和追问统一复核可见性,非目标员工按不存在处理。`ALL` 下目标只作定向提醒。附件支持 txt/md/PDF/Word/Excel/PPT,100MB 内;只有 `READY` 且未绑定、属于当前租户和上传人的文件才可发布。`allowDownload=false` 时员工端不返回下载入口,后端 `/content` 也拒绝;`/view` 返回受控提取文本并记录独立查看审计,客户端叠加含内部账号和分钟的动态水印。水印用于追溯,不等于阻止截屏或泄露;下载副本水印仍须按文件格式另行实现和验收。主题标签仅由固定关键词规则或人工修正产生,均保存可在原文逐字找到的短依据,且绝不参与授权。文件岗位解读继续返回有原文依据的 `insightStatus/defaultPerspectiveCode/perspectiveLabels/perspectives`,不返回原始 OSS 地址。下载、查看和追问每次重新校验 APP 在职身份、租户、消息状态和可见范围;客户端追问仍只传 `broadcastMessageId`。发布、阅读、文件访问和撤回保持幂等与审计约束;管理接口只允许 `superadmin` 或 `hr_operator`。当前仍不包含消息修订、撤回后补推或短信/电话强触达。 |
| 员工直通车 `/pages/user/direct/index`、管理端 `/content/direct` | 员工 `GET /api/aihr/direct/channels`、`POST /api/aihr/direct/feedback`、`GET /api/aihr/direct/mine`、`GET /api/aihr/direct/mine/{id}`;处理端 `GET /api/aihr/direct/admin/feedback`、`POST /api/aihr/direct/admin/feedback/{id}/reply`;处理人配置 `GET /api/aihr/direct/admin/handlers`、`GET /api/aihr/direct/admin/handlers/{channelCode}/candidates`、`POST /api/aihr/direct/admin/handlers/{channelCode}`、`POST /api/aihr/direct/admin/handlers/{channelCode}/remove` | 员工可选择总裁、财务、人力、审计、运营并点对点提交;反馈内容支持语音转文字输入(复用 `/api/ai/asr`;文字为主路径,录音不可用或权限失败仅提示改文字输入)。总裁/审计默认匿名;匿名仅表示业务处理界面不显示提交人,系统仍保存内部账号和姓名快照供本人查询、幂等与审计。`direct_president/direct_finance/direct_hr/direct_audit/direct_operations` 仅处理各自频道,`superadmin` 可处理全部;列表、详情和回复均由服务端按角色收窄。只有 `superadmin` 可维护五通道处理人;候选人由服务端限定为当前租户已启用的后台用户并排除 APP 用户、超级管理员和已绑定用户,写请求必须回传当前 `tenantId` 防止租户上下文漂移;建议每个通道至少配置 2 人,但系统不自动绑定账号。一个反馈只允许一次正式回复,员工可在“我的反馈”查看状态和回复;当前不扩展为工单 SLA、转派或多轮聊天。 |
| 成果投稿(当前页面名“工作上报”)`/pages/user/report/index` | `POST /api/aihr/work-report/organize`、`POST /attachment`、`POST /reports`、`GET /reports/mine`;主管/运营另有列表和审核接口 | 只承载 CASE/VIDEO/SOP/KNOWLEDGE 四类投稿。会话式页面、无状态整理、附件、幂等正式提交和历史状态已部署;审核通过不自动入知识库。当前整理服务不读取图片/视频内容,只把附件名称作为不可信元数据;不得与日常工作记录或“今日工作成果”混用 |
| 资料处理 `/knowledge/processing` | `GET /api/knowledge/processing/overview`、`POST /api/knowledge/doc/upload-async`、`GET /api/knowledge/doc/upload-items`、`POST /api/knowledge/doc/upload-items/{id}/retry`、`POST /api/knowledge/doc/processing-tasks/{attachmentId}/retry`;运维 `GET /actuator/health/aihrUploadCapacity` | 已接入解析任务状态聚合;页面只保留“批量导入”,接口暂存+入队即秒回,后台 worker(单实例并发 2)逐条解析/归类/向量化;ZIP 在 worker 内安全解压后把支持的子文件继续入同一批次队列,页面按批次轮询进度。单文件上限 500MiB、ZIP 解压总量 2GiB、失败暂存保留 72 小时;生产暂存盘通过 `AIHR_UPLOAD_STAGING` 指定,默认可用空间健康门禁为 10GiB。零片段媒体不再记为完成或永久“等待解析”,而是保留为可重试失败;管理端可从原 OSS 文件重新入队,无需用户重复上传;不提供浏览器目录选择或服务端目录导入入口。约 600 名用户不等于 600 并发,峰值、P95、错误率和压测模型仍须另行签认 |
| 组织人员同步 | `POST /api/aihr/org/sync` | 从开放组织同步系统的 `/api/open/v1/sync/snapshot` 拉取 `company/department/employee/employee_project_assignment` 快照,分页参数使用 `limit`;员工手机号只落 `person_phone` 用于移动端身份映射,不在组织列表响应暴露;岗位识别 `position/job_title/post/job_name/role/title` 等字段。`dryRun` 必须显式传入 `true`(预检)或 `false`(写入),省略或传 `null` 直接拒绝;默认 `replaceExisting=true`,写入前必须先运行 `{"dryRun":true}`。dry-run 不访问本地快照表、不执行 DDL/写库,返回 `phoneLinked/maskedPhone/suspectText/warnings` 且 `syncedCount=0`。非 dry-run 写入(含 `replaceExisting=false`)遇到脱敏手机号一律默认拒绝,避免空值覆盖本地登录身份;覆盖写入遇到员工被跳过、手机号不完整、疑似乱码或同项目重复关系时同样默认拒绝;`allowPartialReplace=true` 只能在身份字段契约一致且异常逐项确认后使用,不能绕过主体身份漂移或重复成员关系,此时脱敏员工的本地既有有效手机号会被保留而非清空。相同员工可由多条有效项目分配展开为多个项目成员行,唯一约束为 `tenant_id + project_code + ext_party_id`。2026-07-25 生产安全状态:既有快照 3417 行、2938 人在职、3392 个手机号映射;姓名已按稳定 `employee_id` 定向补齐,未执行全量覆盖。当前上游返回 3424 名员工、3398 个脱敏手机号和 1 条重复项目成员关系,优先字段 `employee_number` 仅匹配既有主体 `7/3417`,稳定 `employee_id` 匹配 `3417/3417`;全量覆盖仍只允许 dry-run,日常岗位变化使用下方增量接口。 |
| 组织人员增量同步 | `POST /api/aihr/org/sync-changes` | 主动拉取上游 `/sync/changes?resource_type=employee`,即使事件没有进入 outbox、`changed_fields` 为空,也会按 `resource_id` 回源 `/employees/{id}`,并结合任职快照只替换受影响员工。主体固定使用稳定 `employee.id`;上游只返回脱敏手机号时保留本地既有有效手机号,上游显式清空手机号时同步清除本地值(对应移动端登录身份失效),手机号字段整体缺失则拒绝写入;范围字段明确不一致视为迁出,写入时删除该员工本地旧记录,范围字段缺失则保守跳过不删。任职快照失败、资源 ID 不一致或项目关系重复时拒绝写入。首次调用必须传 `sinceTime`,后续可传响应的 `nextCursor`;仍须先 `dryRun=true` 再以相同起点执行 `dryRun=false`,且只处理当前租户有效组织绑定范围。 |
| 移动端手机号登录 | `GET /resource/sms/code`、`POST /auth/mobile/sms-login` | 已复用 sms4j 阿里云配置 `config1` 和 RuoYi `sms` 授权策略;移动专用接口固定服务端默认租户,客户端不传也不能选择 `tenantId`。dev 默认 `aihr.sms.verification-enabled=false`,移动 APP 客户端可只提交手机号,便于本地自动化;只有同时设置后端 `AIHR_SMS_VERIFICATION_ENABLED=true` 与前端 `VITE_SMS_VERIFICATION_ENABLED=true` 才恢复验证码控件和校验。启用后,验证码按“默认租户 + 手机号”隔离,并在手机号粒度的分布式锁内完成校验:仅匹配成功才消费,输错不会作废原验证码。生产默认保持验证码校验开启。移动端令牌只关联 `app_user`;若同一手机号存在任何后台/系统账号,一律拒绝登录;手机号完全不存在时才自动注册 `app_user`。固定码仍仅用于显式开启验证后的联调;生产固定码必须同时设置 `AIHR_SMS_DEV_FIXED_CODE` 与 `AIHR_SMS_PROD_FIXED_CODE_ENABLED=true`。 |
| 用户侧三端首页 `mobile-uni` hash 路由;旧 `/h5/user`、`/h5/candidate`、`/h5/supervisor` 兼容重定向 | `GET /api/aihr/mobile/home/{role}` | 未登录请求只返回不读取租户业务统计的公开首屏 seed;已登录移动端请求自动携带 `Authorization/clientid`,才返回员工/主管真实统计;移动端本地 fallback 保演示 |
| 移动端登录后角色识别 | `GET /api/aihr/mobile/me` | 认证后仅以手机号精确查询 `person_phone`(`activeByMobilePhone`,不使用组织模糊搜索);`position_level` 为“主管/项目经理”时进入主管端,否则进入员工端。响应返回去重后的 `projects[]` 供页面按项目名称选择,不要求用户输入或记忆编码;同一人员在多个项目的快照行按 `tenant_id + project_code + ext_party_id` 保留。登录身份缺失、组织查询异常或没有有效在职主体时一律明确失败关闭,不再静默回退员工端。 |
| 移动端员工训练与主管复盘闭环 | 员工复用 `POST /api/train/practice/start`、`/turn`、`/finish`,查询 `GET /api/aihr/mobile/practice/history`、`/practice/mistakes`、`/profile`;训练完成后提交 `POST /api/aihr/mobile/practice/satisfaction`;主管查询 `GET /api/aihr/mobile/practice/team`、`/practice/alerts`、`/practice/reviews`、`/practice/reviews/{id}`,标记 `POST /api/aihr/mobile/practice/reviews/{id}/reviewed`,指派 `POST /api/aihr/mobile/practice/assignments` | 员工端登录后带 `Authorization` 与 `clientid` 调用;服务端固定身份和 `mode=mobile` 后才写入 `aihr_practice_session` 并进入员工/主管统计。员工历史、错题、画像、成长进度、晋升证据和满意度均只使用当前认证手机号经服务端验证得到的受控历史别名;后台内容/任务运营只通过独立 `/api/train/practice/**` 契约,不得向 `/api/aihr/mobile/**` 传入外部 ID 读取个人或主管数据。主管 `/practice/team` 的范围只含已验证外部 ID;历史训练或任务若存的是同一员工的旧手机号,只在该手机号能无碰撞规范化到团队外部 ID 时才展示/统计,避免把另一人的外部 ID 误并入团队。管理端 `mode=preview` 仅是运营调试记录,不能伪装成员工训练、满意度、复盘、成长或试点证据。满意度接口只接受本人已完成训练的 1-5 分,意见脱敏后落库,未填写不补默认值。错题本按员工本人聚合低分/红线回合,并关联已有 `retry` assignment,不伪造错题结论。主管 `/practice/team` 在同一项目权限范围内额外返回 `mistakes` 聚合,按场景/归因统计次数、影响人数、平均分和最近发生时间;普通员工返回“无主管权限”。主管接口以后端当前登录手机号先解析为在职组织外部 ID,仅允许岗位为“主管/项目经理”的账号,并按租户和项目范围返回真实成员、全状态训练记录及非 daily 专项。复盘标记只允许首次 `待复盘 -> 已复盘` 创建后续专项,并发重复提交幂等;可带 `incentivePoint` 写入贡献度。每日三题正式只对 `hire_date` 在当前日期前三个月内的在职员工派发,且只会从已发布、已启用、风险审核完成的同岗位场景题库取题;选择时优先补最低未覆盖的成长层/能力项,仅作为训练推荐,不自动调整职级或形成硬性解锁。没有入职日期时不使用训练次数推断,开发 Demo 的旧回退只有在 `dev/local` profile 且由 `aihr.practice.allow-legacy-daily-drill-fallback` 显式开启时生效,生产 profile 强制关闭。 |
| 移动端候选人闭环 | 页面拆为 `/pages/candidate/index/index`、`/interview/index`、`/materials/index`、`/progress/index`、`/study/index`;面试复用 `POST /api/recruit/interview/start`、`/answer`、`/finish` 和 `GET /records`;资料 `POST/GET /api/aihr/mobile/candidate/materials`;预习 `POST /api/knowledge/search`;HR 审核 `GET /api/aihr/hr/candidate/materials`、`POST /api/aihr/hr/candidate/materials/{id}/review` | 候选人端登录后带 `Authorization` 与 `clientid` 调用;首页只按当前手机号对应的真实面试记录和最新资料状态分流,不读取公开 home seed。APP 候选人身份以后端登录手机号为准,前端 `candidateId/candidateName` 只作非 APP 场景兼容参数;面试支持文字作答和复用 `/api/ai/asr` 的录音转文字/选音频,`/answer` 可带可选 `answerAudioOssId`,服务端只接受当前候选人名下处于 `staged/bound` 状态的音频并将 `ossId` 写入问题快照,不向客户端暴露原始 OSS URL;`/answer` 与 `/finish` 还会校验当前 APP 手机号与启动会话的候选人 ID 一致,未知或他人会话直接拒绝;面试拉题/评分走真实模型优先;资料写 `sys_oss`/MinIO 和 `aihr_candidate_material`,HR 审核后进度页同步三态;岗前预习查询正式 SOP,不兜前端示例答案 |
| 每日题反馈与常见难题 | `POST /api/aihr/mobile/practice/assignments/{id}/feedback`、`GET /api/aihr/mobile/practice/difficulties`、`POST /api/aihr/mobile/practice/difficulties/assign` | 反馈区分题目有用性与答案正确性;仅认证 APP 主管可按其项目范围聚合岗位、时间、次数、人数和均分,并基于难题创建专项。管理端不得调用移动主管接口;后台人工任务只走受角色保护的 `/api/train/practice/assignments/manage/**`。 |
| 岗位考试 | 员工 `GET /api/aihr/mobile/exams`、`GET /exams/{id}`、`POST /exams/{id}/submit`;主管 `GET/POST /exams/supervisor`、`GET/PUT /exams/supervisor/{id}`、`POST /exams/supervisor/{id}/publish`、`GET /exams/supervisor/{id}/results`、`POST /exams/supervisor/question-bank/draw` | 主管按项目范围选择真实员工、组卷、发布和查成绩;员工只读本人目标考试。题库只有 `APPROVED + enabled + exam_enabled` 的题目可抽取,抽题请求 ID 会冻结岗位/题型/题数与题目快照,配置变更复用同一 ID 会拒绝;保存试卷时服务端重新加载快照并保存来源题目 ID、版本和 hash,后续题库编辑不改写已发布试卷。发布和提交接受请求 ID 并保持幂等,不得重复生成成绩或奖励。 |
| 正式学习中心与任务 | 管理端 `/api/train/practice/onboard-tasks/manage`、`/materials`、`/exams`、`/batch`;员工 `GET /api/aihr/mobile/onboard/tasks`、`POST /api/aihr/mobile/onboard/tasks/{id}/complete` | 管理端按租户、项目、规范岗位和在职外部 ID 派发正式学习任务;材料只允许当前租户 `READY` 附件并保存标题/版本快照,任务支持一次/每日/每周/月度周期、起效时间、截止时间、进度和到期提醒。`MANUAL_CONFIRM` 才能人工确认,`EXAM_PASS` 只能由关联考试通过完成;重复请求 ID 返回逐人 `DUPLICATE`,配置不一致拒绝。员工只读本人已生效任务,原文继续经受保护资源接口鉴权;“每日三题”仍留在练习入口,不混入学习中心主任务列表。 |
| 开放问题榜与积分 | 移动端 `/api/aihr/community/questions`、`questions/{id}`、`questions/{id}/answers`、`questions/{id}/best-answer`、`points/me`;后台 `/api/aihr/question-admin/**`、`/api/aihr/incentive-admin/rules` | 问题、答案、最佳答案和奖励全程按租户/身份授权;重复选优不重复入账。奖励只有积分与学习学分,不代表现金或提现 |
| 未解决反馈转人工 | `POST /api/aihr/community/questions/from-answer-feedback`,仅提交 `requestId`、`idempotencyKey` | 服务端重读本人 `down` 反馈和查询审计,问题固定进入 `PENDING`;重复请求返回同一问题,不自动公开或奖励,客户端不得提交重构答案、引用或身份 |
| 全网 AI | 员工 `GET /api/aihr/web-ai/capabilities`、`POST /api/aihr/web-ai/query`;提供方管理 `/api/aihr/web-search/providers/**` | 与企业知识问答分入口和来源。仅接受公网 HTTPS 提供方,地址或密钥变更后必须重新连接测试;未配置/未启用时返回明确不可用,不生成假答案 |
## 数字师傅 Agent 接入
移动端“问”只调用 Agent,旧 `/api/knowledge/query`、`query-media` 和 `/api/aihr/web-ai/**` 仅作底层/兼容能力。示例中的 token、会话、项目和草稿号均为占位值:
```bash
API_BASE=${API_BASE:-https://wygj-api.localhost}
TOKEN='REPLACE_WITH_MOBILE_ACCESS_TOKEN'
curl -fsS "$API_BASE/api/aihr/agent/messages" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"question":"我今天有什么训练任务","conversationId":"<conversation>","contextVersion":1,"projectCode":"<authorized_project>","externalConsent":false}'
curl -fsS "$API_BASE/api/aihr/agent/messages/media" \
-H "Authorization: Bearer $TOKEN" \
-F 'file=@/path/to/image.jpg' \
-F 'question=图中是什么宠物' \
-F 'externalConsent=false'
```
文字问题去空白后必须为 1–1000 字;媒体接口的 `question` 为空时使用“请分析附件内容”。请求不接受身份、角色、`toolCode`、知识空间 ID、SQL 或任意 URL。响应继续使用若依信封,`data.status` 只会是 `COMPLETED/NEEDS_INPUT/NEEDS_CONFIRMATION/NO_EVIDENCE/FORBIDDEN/UNAVAILABLE/FAILED`,按钮分别读取 `actionDraft/clarification/resources/nextActions`,不得解析 `answer` 猜状态。
政策边界在检索前由服务端确定性执行:明确存在版本/规则冲突时返回 `NEEDS_INPUT`,不得自行裁决或拼接;明确无依据时返回 `NO_EVIDENCE`,不得用经验补全政策事实;跨项目、他人敏感信息、无权附件或未发布制度返回 `FORBIDDEN`。移动端 Agent 对涉及制度、权限、费用、安全职责或必留记录的问题,服务端会在规划改写前固定为正式政策模式,客户端不能选择或绕过;MySQL 模糊/全文召回、Qdrant 命中回表和最终引用装配均只接受 `aihr_knowledge_source_governance` 中当前有效的 `FORMAL_POLICY + APPROVED` 附件,并校验审批时 `docId` 快照一致、版本和原文件 SHA-256 登记完整。已覆盖的试点政策领域还会在进入模型前和最终引用装配时执行问题领域锚点相关性复核,只有来源合规且与问题领域匹配的片段才能参与回答;返回客户端的引用摘要围绕同一命中锚点截取,不能只展示无关的文档开头。正式问题不混入个人/项目记忆;治理表缺失、未审批、已失效、版本或哈希为空、领域不相关,以及最终引用重校验失败时统一返回 `NO_EVIDENCE`。普通“经验话术怎么说”等建议问题仍可检索经授权经验材料,但不得包装为制度。访谈、经验萃取、seed 或 AI 输出不得登记为正式政策来源。
全网意图在 `externalConsent=false` 时以业务成功响应返回 `NEEDS_INPUT`,用户同意后原请求携带 `externalConsent=true` 重试。确认式记忆先取得 `actionDraft.draftId`,再提交卡片中可编辑的完整 `draft`:
```bash
curl -fsS "$API_BASE/api/aihr/agent/actions/<draftId>/confirm" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"expectedVersion":1,"idempotencyKey":"<unique-key>","draft":{"category":"FOLLOW_UP","title":"业主回访","summary":"3栋1201待回访","occurredAt":"2026-07-24T09:00:00+08:00"},"enableReminder":false,"saveScope":"PRIVATE"}'
curl -fsS -X POST "$API_BASE/api/aihr/agent/actions/<draftId>/dismiss" \
-H "Authorization: Bearer $TOKEN"
```
校验失败返回业务码 `400`;角色/工具/项目/知识空间越权返回 `403`;会话版本、候选版本或幂等冲突返回 `409`;草稿票据 30 分钟过期后必须重新生成。Agent 审计只记录最小路由元数据,不保存上述问题、回答或附件。
### 练模块身份映射
#### 训练层级确认补充契约
- 主管在 `POST /api/aihr/mobile/practice/reviews/{id}/reviewed` 的既有复盘请求中可选传 `confirmedGrowthLevel`,仅可为 `中级` 或 `高级`。服务端先按当前登录的 APP 主管身份及项目范围校验该会话,再锁定并读取该会话的 `growth_level`:只允许 `初级 → 中级`、`中级 → 高级`,其他值或跨级均返回业务错误。
- 确认只会写入该 `aihr_practice_session` 的 `growth_confirmation_level/growth_confirmed_by/growth_confirmed_time`,并随主管复盘详情和员工 `GET /api/aihr/mobile/profile` 的 `growthConfirmations[]` 回显。它是可追溯的训练带教证据,不创建独立人事记录,不自动变更职级、薪酬、晋升、上岗或资格结论,也不替代待业务确认的正式解锁阈值。
移动端登录主体只按认证手机号精确匹配 `person_phone`;`/me`、问师傅主体和移动训练入口均使用该精确映射,不得回落到组织模糊搜索。`/api/aihr/mobile/**` 的员工历史、画像、成长进度、任务、主管带教和对练录音下载只接受 APP 身份:员工端忽略客户端 `extPartyId`,主管端从当前 APP 手机号解析其组织外部 ID;`SYS_USER` 不得借这些接口传入员工标识读取、复盘、派发、聚合个人训练数据或按 OSS 对象编号下载录音。管理端运营预览固定为 `operator:{userId}` 与 `mode=preview`,不进入员工历史、主管复盘、成长或试点统计;后台仅通过受角色保护的 `/api/train/practice/**` 内容与任务运营契约工作。两个身份来源绝不互作替代查询键。训练任务的读取、启动、完成、每日题、画像、成长进度和晋升证据由服务端按该来源生成受控的历史别名集合,客户端不能传入替代身份。主管团队范围只保存外部 ID;旧手机号存储的记录仅当它唯一映射到一个外部 ID,且该外部 ID 在全部在职快照中也只映射到该一个手机号时,才可规范化到团队范围。规范化结果必须在团队列表、详情、复盘、音频授权与后续任务创建中复用;任一碰撞或多映射都返回无身份、拒绝猜测或读写任务。新任务仅使用唯一映射后的存储身份,旧任务仅能在同一受控别名集合内读取。
## 后端落点
- 业务模块:`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`
当前管理端 AI 面试和案例沉淀已改为真实模型/ASR 优先;移动端首页和部分演示态数据仍保留 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`,AI 面试结果 DDL 在 `backend/script/sql/aihr_interview_result_mysql8.sql`,候选人资料 DDL 在 `backend/script/sql/aihr_candidate_material_mysql8.sql`,组织人员快照表在 `backend/script/sql/aihr_org_snapshot_mysql8.sql`,本地 reset 带 2 个住宅项目 22 人 seed;配置开放组织同步系统后用 `POST /api/aihr/org/sync` 覆盖为外部快照。
直接打后端 `/api/**` 通常需要登录后的 `Authorization: Bearer <access_token>`;浏览器内通过已登录前端和 `/dev-api` 代理访问。移动端登录接口为 `POST /auth/mobile/sms-login`,dev 默认请求 `{ phonenumber }`,显式开启验证码后请求 `{ phonenumber, smsCode }`;内部固定使用 app 客户端 `428a8310cd442757ae699df5d894f051` 和 `sms` grant。手机号不存在时会创建 `app_user`,用户名为手机号,备注为“移动端短信自动注册”。移动端首页接口 `GET /api/aihr/mobile/home/{role}` 仍保留 `@SaIgnore` 以保证真正未登录的首屏可用,匿名分支只返回不读取业务统计的公开数据;已登录请求必须是有效 `app_user`,并按认证手机号校验员工/主管身份后返回真实统计。已登录但身份缺失、后台账号误用、组织查询失败或客户端收到未知角色时均明确失败关闭,不得静默回到公开数据、默认员工端或旧岗位缓存。
阿里云短信复用 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助手
```
候选人资料上传最小后端边界:
| 能力 | 后端接口 | 处理 |
|---|---|---|
| 候选资料上传 | `POST /api/aihr/mobile/candidate/materials` | `multipart/form-data` 字段 `file`,参数 `candidateId/candidateName/materialType`;需移动端登录返回的 `Authorization` 与 `clientid`;APP 用户场景后端强制使用登录手机号和派生候选人名,不信任前端传入身份;服务端与移动端共同限制 PDF/Word/JPG/PNG/WebP,文件名会去掉路径和控制字符;文件用 `ISysOssService` 写 `sys_oss`/MinIO,关系写 `aihr_candidate_material`,状态默认 `待审核`,单文件 20MB |
| 候选资料列表 | `GET /api/aihr/mobile/candidate/materials?candidateId=` | 返回前 20 条 `{id, ossId, fileName, materialType, fileSize, status, time}`,供 `/h5/candidate` 的“已提交资料”展示;APP 用户场景忽略传入 `candidateId`,只返回登录手机号对应资料 |
| HR 资料审核 | `GET /api/aihr/hr/candidate/materials?status=`、`POST /api/aihr/hr/candidate/materials/{id}/review` | 管理端 `/recruit/interview` 展示前 50 条,可按状态筛选;审核请求体 `{status}`,只接受 `待审核/已通过/已驳回`;接口只允许管理端超级管理员或 `hr_operator` 角色访问 |
SOP 文档上传第三片已经落最小后端边界:
> 说明(2026-08-01):下表中仍保留的“直接写入 `aihr_knowledge_fragment`/机会性向量化”是历史兼容实现说明,不代表经过质量批准。批量资料和视频必须使用 `POST /api/knowledge/doc/upload-async`,先进入不可变候选 asset/version/chunk revision 和 `REVIEW_PENDING/QUARANTINED`;只有人工发布后才投影到生产 fragment、数据集成员和 Qdrant outbox。同步 `POST /api/knowledge/doc/upload` 仅供 SOP 单文件即时预览,不得作为批量生产导入入口。新代码或运维脚本不得绕过 `/api/knowledge/quality/assets/{assetId}/publish`。
| 能力 | 后端接口 | 处理 |
|---|---|---|
| 文档上传解析 | `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`;管理端该接口 timeout 为 180s,避免 PDF 同步解析/归类/向量化接近默认 50s 时被客户端断开 |
| 图片视觉 OCR | 同一上传/导入链路 | 上传图片时,若启用了 `aihr_model_config.category='vision'`(无则回退 `category='chat'`)的模型,服务端先读取图片元数据并通过源采样把长边限制到 2048、像素限制到约 419 万,再统一压缩为不超过 5MB 的 JPEG data URL 调用供应商 OpenAI-compatible `/chat/completions`。模型未配置、HTTP/网络/超时错误分别写入安全原因码;OCR 成功但无可用文字时 attach/queue 均记为可重试失败(status=3、0 片段),由管理端从原 OSS 重试,不再显示成永久“等待解析”。不依赖 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 融合后交给 `category='rerank'` 模型(如硅基流动 bge-reranker-v2-m3)按语义相关性重排;rerank 未配置或失败保持 RRF 顺序,Qdrant 或外部向量接口不可用时保留关键词/全文检索。命中后若 chat 模型可用,自动生成结构化轻概要写入响应 `answer/keyPoints/cautions`(≤60字直答+3-6要点+0-3注意;temperature=0,15s 超时,硬约束不得编造、答不了明示无依据);概要失败回退片段原文拼接,检索不报错 |
| 检索总结卡 | `POST /api/knowledge/summary-card`(JSON `{queryText, category, fragmentIds}`) | `fragmentIds` 必须来自当前回答且最多5条;服务端按当前身份与知识空间重新鉴权后,只使用这些引用片段生成 `{title, steps[], objections[], scripts[], reminders[]}`,不再二次检索。chat 模型未配置、调用失败或未返回固定「问题/建议/标准」三段结构时抛明确错误供前端重试,不把原文冒充核心步骤。移动端查SOP结果页「生成总结卡」按钮触发,底部弹层展示并可 TTS 朗读 |
| 解析状态聚合 | `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`、`POST /api/knowledge/doc/import-tasks/{id}/cancel` | 启动后台目录导入并返回任务;任务写入 `aihr_knowledge_import_task`,调用方可查询总数、成功数、失败数、当前文件和进度,运行中任务可取消;仅保留给运维/调试,不在资料处理页暴露 |
| 批量异步上传 | `POST /api/knowledge/doc/upload-async`(multipart `file`+`category`+`batchId`)、`GET /api/knowledge/doc/upload-items?batchId=`、`POST /api/knowledge/doc/upload-items/{id}/retry`、`POST /api/knowledge/doc/processing-tasks/{attachmentId}/retry` | 上传只做暂存(`aihr.upload.staging`,默认 `./.data/staging`)+ 写入 `aihr_knowledge_upload_item`(0待处理/1处理中/2完成/3失败)即返回;`source_attach_id` 记录从原 OSS 重试的附件。文档、图片、视频和 ZIP 单文件均 ≤500MB,生产 Spring/Undertow multipart 上限为单文件 500MB、请求 520MB,Tika 文档输入流上限同步为 500MiB;worker 限制最多 1000 个 ZIP 子文件、单个受支持子文件 ≤500MB、解压总量 ≤2GB,拒绝嵌套 ZIP/不安全路径并忽略未知格式;ZIP 文件名优先按 UTF-8 解码,旧版中文 Windows ZIP 自动回退 GBK;同名子文件自动追加编号并以单条批量写入入队;只有片段数大于 0 才记为完成并删除暂存文件,零片段或安全媒体错误保持失败态供重试;卡住 30 分钟(视频 120 分钟)由清扫重置,失败暂存文件保留 72h;同步接口 `POST /api/knowledge/doc/upload` 不支持 ZIP,保留给 SOP 页单文件即时预览且仍按 100MB 契约执行 |
| 视频解析 | 走批量异步上传,支持 `.mp4/.mov/.avi/.mkv/.webm/.m4v`,单文件 ≤500MB、时长 ≤60 分钟 | 依赖服务器 ffmpeg/ffprobe;抽音轨按 5 分钟分段调 asr 模型转写(`[mm:ss]` 时间戳),按 30s 间隔(≤40 帧)抽关键帧调 vision 模型提取画面文字(相邻重复画面去重);两类文本合并后走归类/切片/向量化;无音轨、ASR 无文本且 vision 无结果时以 `MEDIA_NO_TEXT` 进入可重试失败态,不冒充仍在排队;视频同时只加工 1 个 |
模型能力第二片已经落最小后端边界:
| 能力 | 后端接口 | 处理 |
|---|---|---|
| 模型供应商 | `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;`category` 支持 `chat/vector/rerank/asr/tts/vision` |
| 模型探针 | `POST /api/aihr/model/chat` | 数据库配置后走 OpenAI-compatible `/chat/completions`,否则 seed fallback;请求携带的模型名在库中不存在时(如前端 seed 占位行 `gpt-4o-mini`),自动回退到默认已启用的 chat 配置 |
本阶段不修改 `.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` | 服务端资料目录导入根目录 |
组织人员同步配置:
| 配置 | 默认值 | 用途 |
|---|---|---|
| `AIHR_ORG_SYNC_BASE_URL` / `-Daihr.org-sync.base-url` | 空 | 外部开放平台 `/api/open/v1` 前缀,例如 `https://wuye.meihe.cc/api/open/v1` |
| `AIHR_ORG_SYNC_ACCESS_TOKEN` / `-Daihr.org-sync.access-token` | 空 | 已有 Bearer token;为空时尝试用 client credentials 换 token |
| `AIHR_ORG_SYNC_CLIENT_ID` / `-Daihr.org-sync.client-id` | 空 | 开放平台应用 `client_id`,也用于签名头 `X-Client-Id` |
| `AIHR_ORG_SYNC_CLIENT_SECRET` / `-Daihr.org-sync.client-secret` | 空 | `POST /auth/token` 换取访问令牌 |
| `AIHR_ORG_SYNC_SIGNING_SECRET` / `-Daihr.org-sync.signing-secret` | 空 | 可选 HMAC-SHA256 签名密钥;为空时使用 `AIHR_ORG_SYNC_CLIENT_SECRET`,业务请求带 `X-Client-Secret/X-Timestamp/X-Nonce/X-Signature`,签名 path 必须包含 `/api/open/v1` |
本地启动脚本会先加载根目录 `.env.local`,再加载 `backend/.env`。如果 `backend/.env` 沿用开放平台字段名 `client_id` / `client_secret`,脚本会映射为 `AIHR_ORG_SYNC_CLIENT_ID` / `AIHR_ORG_SYNC_CLIENT_SECRET`。
```bash
TOKEN=<登录后 access_token>
API_BASE=${API_BASE:-https://wygj-api.localhost}
curl -fsS "$API_BASE/api/aihr/org/sync" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"dryRun":true}'
curl -fsS "$API_BASE/api/aihr/org/sync" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"dryRun":false,"replaceExisting":true,"allowPartialReplace":false}'
# 仅在 dry-run 已人工确认允许不完整快照覆盖时使用
curl -fsS "$API_BASE/api/aihr/org/sync" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"dryRun":false,"replaceExisting":true,"allowPartialReplace":true}'
# 上游岗位等员工字段发生变化时,先增量预检,再以同一起点局部写入
curl -fsS "$API_BASE/api/aihr/org/sync-changes" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"dryRun":true,"sinceTime":"2026-07-25 16:00:00","pageSize":500}'
curl -fsS "$API_BASE/api/aihr/org/sync-changes" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"dryRun":false,"sinceTime":"2026-07-25 16:00:00","pageSize":500}'
```
## 前端落点
- 管理端前端:`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-uni/`,路由为 `/h5/#/pages/user/today/index`、`/h5/#/pages/user/practice/index`、`/h5/#/pages/user/sop/index`、`/h5/#/pages/user/profile/index`、`/h5/#/pages/candidate/index/index`、`/h5/#/pages/supervisor/index/index`。未登录先走手机号短信登录,登录后 API 优先请求真实业务接口,不复用后台页面路由。语音输入不要新增移动端专用接口,浏览器录音和文件选择兜底都复用 `/api/ai/asr`。
- 员工端训练:底部“练”进入 `/h5/#/pages/user/practice/index`,展示今日任务、每日三题、专项训练、训练前预习卡和训练对话;每日三题支持文字或录音转写,作答接口 `/api/aihr/mobile/practice/assignments/{id}/answer` 可携带 `answer/audioUrl/audioOssId`,服务端只持久化可安全回放的内联 data URL,OSS 音频通过 `audioOssId` 受保护下载;完成后记录写入 `aihr_practice_session`,员工端可提交 1-5 分训练体验和可选意见,员工端展示训练历史和能力画像,主管端展示待复盘列表,点进单条可看评分、话术、导师改写,并可标记“已复盘”。能力画像按 BRD 五维返回任务完成度、话术规范性、情绪管理能力、响应时效、增值转化潜力;暂无工单/时效数据的维度返回 `null`,前端显示“待采集”,不把训练次数当正式绩效指标。
- 候选人端:`/h5/#/pages/candidate/index/index` 的“开始面试/面试练习”复用 AI 面试 API;“补充资料”走候选资料上传接口,成功后展示资料类型和审核状态。管理端 `/recruit/interview` 底部展示“候选资料审核”,支持通过/驳回。
- 演示 fallback 只作为未配置模型或外部接口异常时的降级,不作为真实测试通过证据。
## 验收
```bash
./scripts/demo-check.sh
npm --prefix mobile-uni run build:h5
API_BASE=${API_BASE:-https://wygj-api.localhost}
curl -fsS "$API_BASE/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>
API_BASE=${API_BASE:-https://wygj-api.localhost}
curl -fsS "$API_BASE/api/aihr/model/providers" -H "Authorization: Bearer $TOKEN"
curl -fsS "$API_BASE/api/aihr/model/configs" -H "Authorization: Bearer $TOKEN"
curl -fsS -X POST "$API_BASE/api/aihr/model/chat" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"prompt":"物业管家处理漏水投诉第一步是什么?"}'
```
Qdrant 和向量索引可单独 smoke;重建接口会清空旧 embedding 并重建 collection,只用于本地验证或确认维度不一致后的修复:
```bash
TOKEN=<登录后 access_token>
API_BASE=${API_BASE:-https://wygj-api.localhost}
curl -fsS http://127.0.0.1:6333/
curl -fsS "$API_BASE/api/knowledge/doc/vector-index-status" -H "Authorization: Bearer $TOKEN"
curl -fsS -X POST "$API_BASE/api/knowledge/doc/rebuild-vector-index" -H "Authorization: Bearer $TOKEN"
```
浏览器验收仍按 [DEMO_ACCEPTANCE.md](DEMO_ACCEPTANCE.md) 的四条关键路径执行。
## 知识数据质量准入
文件上传、个人经验转企业知识和 AI 整理案例都先进入候选版本,不直接写入生产检索。质量审核接口仅允许当前租户的 `superadmin` 或 `hr_operator` 调用,租户上下文和审核人均从登录态取得,不接受请求体指定。
```http
GET /api/knowledge/quality/assets?status=REVIEW_PENDING&limit=50
GET /api/knowledge/quality/assets/{assetId}/issues
GET /api/knowledge/quality/assets/{assetId}/conflicts
GET /api/knowledge/quality/index-health
GET /api/knowledge/quality/metrics
GET /api/knowledge/quality/alerts?includeResolved=false&limit=100
GET /api/knowledge/quality/glossary?status=ACTIVE&limit=100
POST /api/knowledge/quality/glossary
POST /api/knowledge/quality/glossary/{id}/approve
POST /api/knowledge/quality/glossary/{id}/reject
POST /api/knowledge/quality/glossary/{id}/deprecate
GET /api/knowledge/quality/rules?status=SHADOW&limit=100
POST /api/knowledge/quality/rules
POST /api/knowledge/quality/rules/{ruleId}/status
POST /api/knowledge/quality/rules/{ruleId}/evaluations
GET /api/knowledge/quality/rules/{ruleId}/metrics
POST /api/knowledge/quality/rules/{ruleId}/samples
GET /api/knowledge/quality/rules/{ruleId}/samples?status=PENDING&limit=100
POST /api/knowledge/quality/rules/samples/{sampleId}/review
POST /api/knowledge/quality/rules/samples/schedule?limit=200
GET /api/knowledge/quality/pipeline-runs?status=ALL&limit=100
POST /api/knowledge/quality/pipeline-runs
POST /api/knowledge/quality/pipeline-runs/{runId}/finish
GET /api/knowledge/quality/golden-datasets?status=DRAFT&limit=100
POST /api/knowledge/quality/golden-datasets
GET /api/knowledge/quality/golden-datasets/{datasetId}/samples?limit=200
POST /api/knowledge/quality/golden-datasets/{datasetId}/samples
POST /api/knowledge/quality/golden-datasets/{datasetId}/freeze
GET /api/knowledge/quality/rules/{ruleId}/acceptance-profiles
POST /api/knowledge/quality/rules/{ruleId}/acceptance-profiles
POST /api/knowledge/quality/rules/{ruleId}/acceptance-profiles/{profileId}/freeze
GET /api/knowledge/quality/rules/{ruleId}/readiness
POST /api/knowledge/quality/conflicts/{conflictId}/review
GET /api/knowledge/quality/assets/{assetId}/privacy-preview
POST /api/knowledge/quality/assets/{assetId}/publish
POST /api/knowledge/quality/assets/{assetId}/withdraw
GET /api/knowledge/quality/legacy/preview?afterAttachmentId=0&limit=50
POST /api/knowledge/quality/legacy/stage?afterAttachmentId=0&limit=50
```
`publish` 必须带人工审核意见、用途、来源权威级别和来源版本。请求中的 `acceptedReasonCodes` 只能确认当前版本仍开放的软提示,服务端会再次统计开放问题,遗漏任何一项都拒绝发布;`HARD` 问题不能在发布动作中接受,必须修订原资料并生成新版本,或通过后续独立的显式纠错流程解决。规范性知识仅接受 `FORMAL_POLICY / COMPANY_POLICY / REGULATION / APPROVED_SOP` 且版本非空;同名已发布资料内容变化会产生 `VERSION_CONFLICT`,规范性知识必须再传 `supersedesAssetId`,服务端验证同租户、同来源名和旧资产仍已发布后,在同一事务内撤回旧版本并发布新版本。
规范化采用版本化的 NFKC、换行、控制字符和空白处理;解析正文和规范化正文作为不可变审计证据保留,不作为下游生产内容。`privacy-redaction-v1` 从规范化正文生成独立的 `redacted_content/redacted_source_name`,记录派生 hash、分类计数、隐私状态和规则版本,并对派生正文及全部派生 chunk 复扫。只有 `CLEAN/REDACTED` 派生版本参与结构化切片、异步语义分析、模型调用、发布和检索;重复文件复用的历史摘要、模型新生成的摘要/标签/归类理由以及上传 snippet 也必须在返回或保存前走同一脱敏规则,不能因 OSS 对象复用而绕过。残留敏感模式写入 `BLOCKED / PII_DETECTED` 并拒绝发布,已完成替换写入 `PII_REDACTED` 证据。`GET /assets/{assetId}/privacy-preview` 只返回脱敏文件名、分类计数和截断后的脱敏正文,不返回原始正文。
按标题、段落、Q&A 与 SOP 步骤切片,只有超长结构块使用 overlap。处理结果只写新版本,不覆盖 raw;`aihr_data_version` 保存 SimHash、结构画像、extractor 和规则版本。精确重复、近重复、语义重复和版本冲突只产生 reason code 与候选关系,不由算法自动删除、批准或合并。语义分析在接入事务外异步执行;PII、提示词注入或已隔离内容不发送到外部 embedding。租户未配置可用向量模型时使用本地 hash 向量,并写入 `SEMANTIC_ANALYSIS_DEGRADED`,不得冒充完整语义检测;状态未达到 `COMPLETE/NOT_REQUIRED` 时发布失败。
制度与专家材料由确定性规则提取 claim 候选,只比较同租户已发布 claim,正反约束或数值不一致写入 `EXPERT_CONFLICT` 和 `aihr_claim_conflict`。审核人通过 `/conflicts/{conflictId}/review` 明确提交 `CONFIRMED/FALSE_POSITIVE/RESOLVED` 及依据;`PENDING_REVIEW/CONFIRMED` 冲突存在时后端拒绝发布。算法和 LLM 均不能自行裁决适用范围、权威来源或替代关系。
发布后才创建 `aihr_knowledge_fragment`、生产数据集成员和向量 outbox;撤回会在同一数据库事务中先删除 MySQL 生产片段、停用数据集成员,再排队删除 Qdrant 文档。向量失败不恢复 MySQL 可检索性。outbox 最多自动尝试 8 次,之后进入 `DEAD_LETTER`;`index-health` 返回待处理、重试、死信数量和最老事件时间,运营必须处理死信而不能把数据库 `PUBLISHED/DEPRECATED` 状态改回去掩盖漂移。每次查询的召回证据写入 `aihr_query_evidence`,记录实际 rank、score、`FULLTEXT/KEYWORD/VECTOR/HYBRID_RRF` 通道和 used 标记,可反查 chunk revision、数据版本、资产、原附件和 OSS 对象。
`legacy/preview` 只读返回下一批 `discovered/sourceAvailable/sourceUnavailable/nextCursor/moreAvailable`,不读取对象正文、不写治理表或索引。`legacy/stage` 按附件 ID 游标小批次纳管历史资料:存在当前租户可验证的 OSS 原件时,服务端从 MinIO 下载到受限临时文件,重新执行解析、质量门禁和切片;没有原件或原件不可读取时,创建 `QUARANTINED / SOURCE_UNAVAILABLE` 的不可变版本。写接口返回 `discovered/staged/reparsed/quarantined/failedAttachmentIds/nextCursor/moreAvailable`。它不从既有 fragment 反向伪造原始来源、不自动批准、不覆盖 MinIO;失败 ID 必须显式复核或从该 ID 前重新运行,只有人工确认来源、版本和适用范围后才能发布。
术语表采用不可变修订:创建接口只产生 `DRAFT`,只有人工 `approve` 后的 `ACTIVE` 版本参与同租户、项目和角色范围内的召回查询扩展;批准新版本会失效同一术语的旧活动版本。当前认证上下文没有可靠区域字段,带 `applicableRegion` 的词条 fail-closed,不参与扩展。`definition` 不进入模型事实上下文,`deprecate` 后立即停止扩展。对应迁移为 `aihr_20260809_knowledge_glossary_mysql8.sql`。AI 整理案例永久保留 synthetic、生成器/模型、prompt、原始音频 OSS、摘要 hash 和人工审核信息,对应迁移为 `aihr_20260810_case_provenance_mysql8.sql`;人工通过不改变其合成身份。
员工“没解决”反馈可携带本轮知识查询 `requestId`。服务端只接受该请求实际召回且属于当前租户的 fragment,创建 `DOWNSTREAM_ANSWER_DISPUTED` 再审问题,不自动撤回资料。后台复核接口 `POST /api/knowledge/answer-feedback/{id}/review` 只接受 `{action: "KEEP"|"WITHDRAW", note: "..."}`:`KEEP` 将争议标为误报并保留资料,`WITHDRAW` 必须由已认证审核人填写理由并调用统一生命周期撤回,随后触发向量 DELETE outbox。
质量监控由 `AihrKnowledgeQualityMonitoringService` 按租户定时扫描。`metrics` 返回资产生命周期、开放问题、待裁决重复/冲突、24 小时查询证据覆盖率、生产来源可追溯率和索引一致性;`alerts` 返回稳定 reason code、严重级别、首次/最近发生时间、累计次数、证据和解决状态。监控只报警和自动关闭已恢复告警,不批准、发布、合并、替代或删除资产。Actuator 健康组件名为 `aihrKnowledgeQuality`;迁移未执行时返回 `UNKNOWN`,存在生产血缘缺口、索引漂移、死信、过期发布资产或查询证据缺口时返回 `DOWN`。对应新增表迁移为 `backend/script/sql/update/aihr_20260808_data_quality_monitoring_mysql8.sql`。
规则演进第一批接口只保存版本化规则草稿、人工/黄金集期望与影子结果的对照、误放行/误隔离指标以及风险抽样复核。规则动作只允许 `FLAG/REVIEW/QUARANTINE`,不允许批准、发布或删除;服务端只允许 `DRAFT -> SHADOW -> PAUSED/RETIRED` 及 `PAUSED -> SHADOW/RETIRED`,在独立自动执行安全门、真实黄金集和灰度回滚能力完成前拒绝进入 `CANARY/ACTIVE`。影子评估和抽样结果不修改 `aihr_data_asset`、质量问题、审核决定或索引。对应迁移为 `backend/script/sql/update/aihr_20260811_rule_evolution_mysql8.sql`。
规则演进第二批新增 `aihr_pipeline_run` 和影子评估自动抽样。`pipeline-runs` 保存同租户的阶段、处理器及版本、输入/输出版本、触发方式、指标、错误和起止时间;完成状态只允许 `SUCCEEDED/FAILED/CANCELLED`,不能把 `PUBLISHED` 伪装成处理结果。定时任务及 `/rules/samples/schedule` 只读取仍处于 `SHADOW` 的评估:误放行/误隔离 100% 进入待复核,其余按 `CRITICAL=100% / HIGH=50% / MEDIUM=20% / LOW=10%` 做稳定哈希抽样,唯一键保证重复调度幂等。自动抽样只创建 `PENDING` 样本和运行证据,`assetMutationCount` 固定为 0;它不修改资产、问题、审核、发布或索引。管理端资料处理页只开放草稿、影子、暂停、退役和人工样本复核,不提供 `CANARY/ACTIVE`。对应迁移为 `backend/script/sql/update/aihr_20260812_pipeline_run_sampling_mysql8.sql`。
规则演进第三批新增版本化黄金集和风险验收门槛。黄金集先创建 `DRAFT`,只能由登录后台人员逐条写入稳定样本键、风险、期望决定、reason code 和人工证据;冻结时生成 SHA-256 内容哈希,此后不可修改,修订必须新建版本。`expectedSource=GOLDEN` 的影子评估必须引用当前租户已冻结的 `goldenSampleId`,期望决定、样本身份和资产版本由服务端从冻结样本加载,客户端不能自称黄金标签。每个规则版本可建立并人工冻结一份风险门槛,`readiness` 分别检查总样本、黄金样本、人工复核、一致率、误放行、误隔离、复核覆盖和待复核差异,返回稳定原因码;`enforcementEnabled` 固定为 `false`,证据就绪不等于允许 `CANARY/ACTIVE`。对应迁移为 `backend/script/sql/update/aihr_20260813_golden_calibration_mysql8.sql`。
## 多租户知识空间统一问答
内部员工端、主管端和管理端统一调用:
```http
POST /api/knowledge/query
Authorization: Bearer <session-token>
Content-Type: application/json
{
"queryText": "漏水投诉第一步怎么处理?",
"spaceCodes": ["yc_property_sop"],
"limit": 5
}
```
内部有效空间由服务端按当前租户、SESSION 应用绑定和用户/角色授权求交集;请求体中的 `tenantId/userId/extPartyId` 不参与身份判断。员工本人和主管团队概况只允许使用固定数据工具:
```json
{"queryText":"我的训练概况","toolCode":"MY_PRACTICE_SUMMARY"}
{"queryText":"团队训练概况","toolCode":"TEAM_PRACTICE_SUMMARY"}
```
外部应用使用独立入口,不能调用数据工具:
```http
POST /api/open/knowledge/query
Authorization: Bearer <api-token>
Content-Type: application/json
{"queryText":"你们提供哪些服务?","spaceCodes":["mt_customer_service"],"limit":5}
```
成功响应包含 `requestId`、`answer`、`citations`、`usedSpaceCodes`、`noEvidence` 和兼容 `legacy` 数据。引用携带 `spaceCode/sourceType/docId/title/snippet/fragmentId`,可查看原始出处的 `DOCUMENT` 引用另带 `mediaType/detailRef/locatorSummary`;`detailRef` 是短期不透明引用,只能交给受认证的引用详情接口重新鉴权,客户端不得解析或拼接附件身份。客户端不得把无引用回答包装成有知识依据的正式答案。
空间、空间内分类、授权和应用管理接口统一位于 `/api/knowledge/admin`:`spaces`、`spaces/{id}/categories`、`spaces/{id}/documents`、`spaces/{id}/documents/{attachId}/category`、`spaces/{id}/grants`、`apps`、`apps/{id}/spaces`、`apps/{id}/rotate-token`。分类仅在当前有效租户和当前知识空间内维护;停用分类不能继续归类,删除前必须先移走其知识,分类不会扩大空间授权或调用应用的检索范围。`DELETE /spaces/{id}/documents/{attachId}` 只解绑当前空间成员;若该成员对应已批准或已发布的治理资产,服务端会先以当前人工操作人执行统一 `WITHDRAW`,立即切断 MySQL 生产可见性并通过 outbox 删除向量。其他空间或治理资产仍引用同一 OSS 时原文件不会删除,引用核验失败时也必须保留原件。仅知识平台管理角色可调用,API_TOKEN 创建或轮换后只返回一次明文。超级管理员由“租户管理”进入知识维护时,通过 `GET /system/tenant/dynamic/{tenantId}` 设置服务端动态租户,再由 `GET /system/tenant/dynamic` 回读当前上下文;仅正常状态的已存在租户允许切换。动态租户按当前 Sa-Token 与浏览器页面生成的匿名上下文共同隔离,客户端每次请求回传页面上下文及当前展示租户;两者与服务端不一致时服务端返回 `409` 并拒绝读写,避免多标签页误写。知识平台不接受客户端传入的 `tenantId` 来选租户。完整初始化、令牌保管、legacy 迁移与回滚步骤见 [KNOWLEDGE_PLATFORM_RUNBOOK.md](KNOWLEDGE_PLATFORM_RUNBOOK.md)。
## 工作助手确认式统一采集
本节记录当前已经部署的接口。个人笔记候选可不绑定项目;晨会、巡检、业主服务、画像和线索等项目工作必须由员工从项目名称列表选择当前项目,再由服务端按登录主体复核。项目化短会话、受保护原始媒体绑定、员工成果生成和主管项目成果查询均已在可调用接口表中列出;后续外部线索、工单、考勤接收端仍不得提前写成已送达。
该链路复用上面的短会话请求,不新增另一套问答 session。登录员工说“帮我记一下三栋 3203 需要保洁服务”时,`POST /api/knowledge/query` 在正常回答字段之外可返回;缺少单元不会阻断用户保存:
```json
{
"memoryCandidate": {
"id": 301,
"version": 1,
"status": "DRAFT",
"memoryType": "SERVICE_LEAD",
"targetDomain": "ASSISTANT_CAPTURE",
"draft": {
"buildingName": "3栋",
"unitName": "",
"roomNo": "3203",
"category": "服务线索",
"summary": "需要保洁服务",
"occurredAt": "2026-07-19T14:00:00+08:00",
"followUpAt": null
},
"missingFields": ["unitName"],
"expiresAt": "2026-07-20T14:00:00+08:00"
}
}
```
确认请求只提交用户在卡片上确认或修改的草稿。`PERSONAL_NOTE` 可以不绑定项目;晨会、巡检、业主服务、画像、线索等项目工作必须先由用户按项目名称选择,草稿携带内部 `projectCode`,但服务端仍以当前登录主体的授权项目集合复核,不能把客户端字段当授权依据:
```http
POST /api/aihr/personal-assistant/memory-candidates/301/confirm
Authorization: Bearer <session-token>
Content-Type: application/json
{
"expectedVersion": 1,
"idempotencyKey": "memory-confirm-301-<stable-uuid>",
"saveScope": "COMPANY",
"enableReminder": false,
"draft": {
"projectCode": "PRJ-XHW1",
"buildingName": "3栋",
"unitName": "",
"roomNo": "3203",
"category": "服务线索",
"summary": "需要保洁服务",
"occurredAt": "2026-07-19T14:00:00+08:00",
"workDate": "2026-07-19",
"businessStatus": "PENDING",
"followUpAt": null
}
}
```
服务端在事务中锁定候选,写入 `aihr_assistant_capture` 后才把候选改为 `CONFIRMED`。`PRIVATE` 对应 `deliveryStatus=NOT_REQUIRED`;`COMPANY` 对应 `PENDING`。`DRAFT` 默认 24 小时过期,列表查询强制过滤当前租户与 owner。`GET /api/aihr/personal-assistant/assistant-captures?saveScope=COMPANY&limit=50` 只展示本人的企业流转状态;PENDING 不是已生成工单或已上报成功。状态更新带独立 `idempotencyKey`,相同记录和目标状态可安全重试,跨记录或跨目标状态复用返回 409。`/api/knowledge/query` 可受控召回确认记录,引用域分别为 `PERSONAL / COMPANY / PROJECT_SERVICE`;个人和公司采集按 `tenant_id + owner_user_id` 过滤,旧项目记录继续按项目权限过滤。