feat(aihr): finalize practice growth and access boundaries

This commit is contained in:
2026-07-25 00:42:19 +08:00
parent a90f40e4d8
commit c9d2012ec1
87 changed files with 6804 additions and 2538 deletions
+21 -10
View File
@@ -8,26 +8,28 @@
|---|---|---|
| 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 剧本作剧情锚点),`/finish` 走单次 temperature=0 结构化评分(4 维分+导师改写+点评);移动端 `turn/finish` 校验当前手机号与启动会话归属,未知或他人会话拒绝;模型未配置或调用失败自动回退 seed,契约不变 |
| 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。该契约当前仅完成本地实现与验证,线上尚未有该表。 |
| 三角色对练 `/train/practice` | `POST /api/train/practice/start`、`/turn`、`/finish` | 已接入编排 API;数据库启用 chat 模型后,`/turn` 客户回复按人设走真 LLM 生成(seed 剧本作剧情锚点),`/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}` | 走 OpenAI-compatible audio 接口;生产 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 保持旧客户端可播放;音频下载按员工本人或主管项目范围授权,系统管理端保持后台访问 |
| 案例沉淀 `/knowledge/cases` | `GET /api/knowledge/case/capabilities`、`POST /api/knowledge/case/upload`、`/organize`、`/curate`、`GET /records`、`GET /records/{caseId}`、`POST /records/{caseId}/review`、`GET /records/{caseId}/media` | `/capabilities` 返回服务端判定的案例提交/查看能力,移动端不再向普通员工展示无权提交的素材表单;`/upload` 改为 multipart 真实语音上传并走 ASR,服务端只接受 MP3/WAV/M4A/WebM/OGG/AAC/FLAC,成功后原始音频写入 `sys_oss`,案例记录只保存 `mediaOssId` 供受保护媒体接口读取,不向客户端回传原始 `mediaUrl`;`/organize` 用真实转写调 chat 模型整理案例,未配置模型时按真实 transcript 本地结构化,并从背景外的真实摘要项提取学习点;APP 用户的项目范围从 `aihr_org_snapshot` 登录身份解析,上传、整理、入库、列表和详情均按项目范围校验,未完成正式组织映射时安全拒绝,不接受前端伪造项目范围;员工列表/详情只返回 `已入库` 案例,管理端系统用户保留全局运营视图;移动端和管理端案例详情通过受保护媒体接口回放原始音频,主管/项目负责人可提交脱敏点评;预渲染视频样片仍待正式媒体资产接入 |
| 对练语音 | `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}` | 走 OpenAI-compatible audio 接口;生产 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 身份,后台账号若需回放必须使用未来独立、受审计的管理端契约。 |
| 案例沉淀 `/knowledge/cases` | `GET /api/knowledge/case/capabilities`、`POST /api/knowledge/case/upload`、`/organize`、`/curate`、`GET /records`、`GET /records/{caseId}`、`POST /records/{caseId}/review`、`GET /records/{caseId}/media` | `/capabilities` 返回服务端判定的案例提交/查看能力,移动端不再向普通员工展示无权提交的素材表单;`/upload` 改为 multipart 真实语音上传并走 ASR,服务端只接受 MP3/WAV/M4A/WebM/OGG/AAC/FLAC,成功后原始音频写入 `sys_oss`,案例记录只保存 `mediaOssId` 供受保护媒体接口读取,不向客户端回传原始 `mediaUrl`;`/organize` 用真实转写调 chat 模型整理案例,未配置模型时按真实 transcript 本地结构化,并从背景外的真实摘要项提取学习点;APP 用户必须先以认证手机号精确校验在职 `person_phone`,再由可信主体解析 `aihr_org_snapshot` 项目范围;案例 SQL 不得把 `person_phone` 与 `ext_party_id` 作为替代查询键。上传、整理、入库、列表和详情均按该项目范围校验,未完成正式组织映射时安全拒绝,不接受前端伪造项目范围;员工列表/详情只返回 `已入库` 案例,管理端系统用户保留全局运营视图;移动端和管理端案例详情通过受保护媒体接口回放原始音频,主管/项目负责人可提交脱敏点评;预渲染视频样片仍待正式媒体资产接入 |
| 案例媒体安全 | `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/resources/{attachmentId}/content`、`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` 状态重新取消息;撤回、跨租户或非在职员工均拒绝。原文件/视频仅从当前命中且仍有授权的附件返回,内容接口每次重新计算租户、应用和主体权限;无资源时明确返回空列表,不生成假链接。搜索继续返回 `reviewId/promptVersion`,员工反馈保存评审批次;详细边界见 [专项设计](20260718/问师傅多轮会话与原始资料交付设计.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` 继续兼容;确认记录读写仍使用 `/api/aihr/personal-assistant/**`,旧项目记录继续使用 `/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`,幂等键不得跨记录或目标状态复用。来源文件重新鉴权交付;正式外部接收端未配置前不得显示已送达 |
| SOP 知识底层服务 `/knowledge/sop` 与旧客户端兼容 | `POST /api/knowledge/query`、`POST /api/knowledge/query-media`、`GET /api/knowledge/resources/{attachmentId}/content`、`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`,不得以管理端组织快照的模糊/别名查询作为后备。原文件/视频仅从当前命中且仍有授权的附件返回,内容接口每次重新计算租户、应用和主体权限;无资源时明确返回空列表,不生成假链接。搜索继续返回 `reviewId/promptVersion`,员工反馈保存评审批次;员工学习页按当前 APP 身份读取正式岗前/入职任务,资格证据无正式数据时明确返回 `NOT_CONFIGURED`。详细边界见 [专项设计](20260718/问师傅多轮会话与原始资料交付设计.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`;员工详情可经统一 `POST /api/knowledge/query` 的 `broadcastMessageId` 发起文字追问;管理 `GET /api/aihr/broadcast/admin/messages?pageNum=&pageSize=&status=PUBLISHED|WITHDRAWN|all`(响应含服务端确认的 `tenantId`)、`POST /api/aihr/broadcast/messages`(`{requestId,title,content,expectedTenantId}`)、`POST /api/aihr/broadcast/messages/{id}/withdraw`(`{reason,expectedTenantId}`) | 员工端只返回当前登录租户的 `PUBLISHED` 公开消息,租户和阅读用户均由服务端登录身份取得;阅读表以 `(tenant_id,message_id,user_id)` 唯一约束保证重复打开幂等。详情“问这条消息”只在本地暂存消息 ID,问模块重新读取并由服务端可信解析消息;撤回、跨租户或非在职员工均不可继续追问。管理读写使用服务端当前有效租户(动态租户优先);`expectedTenantId` 只能回显刚读取的服务端范围,不用于选择租户,若另一标签页已切换范围则返回 `409`,避免旧页面误发或误撤回。发布以 `(tenant_id,published_by,requestId)` 幂等:同键同内容重放原 `{id,version:1}`,同键异内容返回 `409`;一个事务内写主消息和不可变 v1 内容快照。管理列表可按已发布/已撤回筛选并返回正文、发布/撤回审计与固定 `currentVersion=1`。撤回原因必填、去首尾空白且最多 500 字符;首次只允许 `PUBLISHED -> WITHDRAWN`,重复撤回成功但不得覆盖原撤回人、时间或原因。管理端将未知发布请求按“有效租户 + 管理员”的 `v2` 本地记录保存,存储不可用时拒绝发起发布。首版不含修订、定向目标、附件或 AI 摘要;管理接口只允许 `superadmin` 或 `hr_operator`。 |
| 银城大喇叭 | 员工 `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}/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}`、`POST /api/aihr/broadcast/messages`、`POST /api/aihr/broadcast/messages/{id}/withdraw` | 发布支持全员或按部门/岗位/人员定向、必读、单个公司文件与异步提炼。文件支持 txt/md/PDF/Word/Excel/PPT,100MB 内;先受控写入 OSS,再异步解析并复用现有模型生成摘要,只有 `READY` 且未绑定、属于当前租户和上传人的文件才可随消息发布。员工详情只返回摘要和受控下载信息,不返回提取全文或原始 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` | 员工可选择总裁、财务、人力、审计、运营并点对点提交。总裁/审计默认匿名;匿名仅表示业务处理界面不显示提交人,系统仍保存内部账号和姓名快照供本人查询、幂等与审计。`direct_president/direct_finance/direct_hr/direct_audit/direct_operations` 仅处理各自频道,`superadmin` 可处理全部;列表、详情和回复均由服务端按角色收窄。一个反馈只允许一次正式回复,员工可在“我的反馈”查看状态和回复;当前不扩展为工单 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` | 已接入解析任务状态聚合;页面只保留“批量导入”,接口暂存+入队即秒回,后台 worker(并发 2)逐条解析/归类/向量化;ZIP 在 worker 内安全解压后把支持的子文件继续入同一批次队列,页面按批次轮询进度、失败可单文件重试;不提供浏览器目录选择或服务端目录导入入口 |
| 组织人员同步 | `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 覆盖写入遇到员工被跳过、手机号不完整、脱敏手机号或疑似乱码时默认拒绝,只有确认 dry-run 结果后显式传 `allowPartialReplace=true` 才允许覆盖。相同员工可由多条有效项目分配展开为多个项目成员行,唯一约束为 `tenant_id + project_code + ext_party_id`;只拒绝同一项目内的重复成员关系。`replaceExisting=false` 不触发不完整快照覆盖闸门,但仍拒绝同项目重复关系。2026-07-15 线上已用 `/api/open/v1` 前缀完成配置、dry-run 和覆盖同步:开放平台返回公司 17、部门 963、员工 3417;生产 `aihr_org_snapshot` 为 3417 行,`phoneLinked=3392`、`maskedPhone=25`、`suspectText=0`,在职 2943、离职 474。该统计发生在多项目成员迁移前;发布迁移后必须重新 dry-run 并核对展开后的项目成员行数。当前剩余 25 人手机号不可用属于上游数据质量问题 |
| 移动端手机号登录 | `GET /resource/sms/code`、`POST /auth/mobile/sms-login` | 已复用 sms4j 阿里云配置 `config1` 和 RuoYi `sms` 授权策略;移动专用接口固定服务端默认租户,客户端不传也不能选择 `tenantId`;验证码按“默认租户 + 手机号”隔离。校验在手机号粒度的分布式锁内完成:仅匹配成功才消费,输错不会作废原验证码。移动端令牌只关联 `app_user`;若同一手机号存在任何后台/系统账号(即使同时存在 `app_user`),一律拒绝登录而不复用或并置身份;手机号完全不存在时才自动注册 `app_user`。`aihr.sms.dev-fixed-code` 非空时不真发短信、验证码固定(dev 默认 `123456`)。prod 默认关闭,试点期只有同时设置 `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` | 认证后按手机号匹配组织快照;`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`。满意度接口只接受本人已完成训练的 1-5 分,意见脱敏后落库,未填写不补默认值。错题本按员工本人聚合低分/红线回合,并关联已有 `retry` assignment,不伪造错题结论。主管 `/practice/team` 在同一项目权限范围内额外返回 `mistakes` 聚合,按场景/归因统计次数、影响人数、平均分和最近发生时间;普通员工返回“无主管权限”。主管接口以后端当前登录手机号映射在职组织快照,仅允许岗位为“主管/项目经理”的账号,并按租户和项目范围返回真实成员、全状态训练记录及非 daily 专项。复盘标记只允许首次 `待复盘 -> 已复盘` 创建后续专项,并发重复提交幂等;可带 `incentivePoint` 写入贡献度。每日三题正式只对 `hire_date` 在当前日期前三个月内的在职员工派发;没有入职日期时不使用训练次数推断,开发 Demo 的旧回退只有在 `dev/local` profile 且由 `aihr.practice.allow-legacy-daily-drill-fallback` 显式开启时生效,生产 profile 强制关闭。 |
| 移动端登录后角色识别 | `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` | 反馈区分题目有用性与答案正确性;主管/管理端按岗位、时间、次数、人数和均分聚合,并可基于难题创建专项;身份和项目范围由服务端解析 |
| 每日题反馈与常见难题 | `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` | 主管按项目范围选择真实员工、组卷、发布和查成绩;员工只读本人目标考试。发布和提交接受请求 ID 并保持幂等,不得重复生成成绩或奖励 |
| 开放问题榜与积分 | 移动端 `/api/aihr/community/questions`、`questions/{id}`、`questions/{id}/answers`、`questions/{id}/best-answer`、`points/me`;后台 `/api/aihr/question-admin/**`、`/api/aihr/incentive-admin/rules` | 问题、答案、最佳答案和奖励全程按租户/身份授权;重复选优不重复入账。奖励只有积分与学习学分,不代表现金或提现 |
| 全网 AI | 员工 `GET /api/aihr/web-ai/capabilities`、`POST /api/aihr/web-ai/query`;提供方管理 `/api/aihr/web-search/providers/**` | 与企业知识问答分入口和来源。仅接受公网 HTTPS 提供方,地址或密钥变更后必须重新连接测试;未配置/未启用时返回明确不可用,不生成假答案 |
@@ -68,6 +70,15 @@ curl -fsS -X POST "$API_BASE/api/aihr/agent/actions/<draftId>/dismiss" \
校验失败返回业务码 `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`