From 7ced39b03b707adfc73618ef12a275a5af251070 Mon Sep 17 00:00:00 2001 From: let5sne Date: Sat, 25 Jul 2026 13:39:23 +0800 Subject: [PATCH] fix(practice): restore scenario content updates --- AGENTS.md | 1 + .../dromara/aihr/service/AihrPracticeSeedService.java | 2 +- .../aihr/service/AihrPracticeSeedServiceTest.java | 10 ++++++++++ docs/DEV_SETUP.md | 1 + 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 6b9fed91..74bc5c60 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -62,6 +62,7 @@ - 移动端手机号登录和短信参数见 `docs/API_INTEGRATION.md`。密钥只放 `.env.local` 或外部环境;dev 可用固定码,prod 默认关闭,试点期必须同时显式开启固定码和值,停用时两者一并清除。手机号不存在时自动注册 `app_user`。 - 移动端训练必须带 APP 登录态并由服务端强制 `mode=mobile`。APP 身份只按认证手机号精确匹配 `person_phone`,运营/组织身份只按 `ext_party_id`;禁止 `OR` 查询、互相后备或客户端指定身份,碰撞/不唯一一律失败关闭。员工历史、画像、证据、复盘和录音只对验证后的 APP 主体开放;管理端仅允许服务端绑定的 `operator:{userId}`、`mode=preview` 运营预览,且不得进入员工历史或试点统计。主管旧手机号只有与在职外部 ID 双向唯一时才可规范化,团队、详情、复盘和音频授权使用同一规则。 - 正式试点 CSV 起止日必填;训练、校准和 SOP 评审按同一窗口、唯一在职身份统计,完训定义为每人至少 10 次。严格预检使用 `AIHR_PILOT_START_DATE`、`AIHR_PILOT_END_DATE` 与 `AIHR_PILOT_STRICT=true ./scripts/demo-check.sh`,不得用 seed、开发身份或四舍五入比率冒充通过。 +- 生产移动端浏览器验收不等待用户提供手机号:从生产 `aihr_org_snapshot` 只读选择目标岗位下 `employment_status='active'`、11 位 `person_phone` 且手机号与 `ext_party_id` 双向唯一的账号;手机号只在当前自动化进程内使用,不写入命令日志、截图、报告或仓库。固定验证码启用时也必须先请求 `/resource/sms/code`,再用固定码登录;不得因未手工提供手机号或未先请求验证码而把生产验收标为阻塞。 - 候选面试复用 `/api/recruit/interview/**`;补充资料走认证的移动端接口,先写 `sys_oss`/MinIO,再写 `aihr_candidate_material`,状态仅为 `待审核/已通过/已驳回`,由管理端 `/recruit/interview` 审核。上传接口不得放入 class-level `@SaIgnore`。 - 后端 API 对接先看 `docs/API_INTEGRATION.md`。三角色对练 `/turn`/`/finish` 使用 `AihrPracticeLlmService`,必须保留“未配置/失败回退 seed”的降级链。 - 对练语音复用 `/api/ai/asr` 与 `/api/ai/tts`,TTS 保持旧 `voice` 兼容并支持角色音色。浏览器录音不可用或权限失败时,用 `audio/*` file input 继续走同一 ASR,不退回纯文字;新增 multipart 外发同样清洗 filename/contentType 的 CRLF。 diff --git a/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java b/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java index 89b9bc12..65b98738 100644 --- a/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java +++ b/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java @@ -504,7 +504,7 @@ public class AihrPracticeSeedService { customer_persona, opening_text, explicit_need, hidden_need, irritants, delights, sop_refs, success_criteria, strategy, goal, trainee_name, trust_start, trust_end, rounds_json, content_version, content_hash, enabled, sort_order, create_time, update_time) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '张三', 60, 80, ?, ?, ?, ?, 100, ?, ?) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '张三', 60, 80, ?, ?, ?, ?, 100, ?, ?) ON DUPLICATE KEY UPDATE scenario_name = VALUES(scenario_name), position = VALUES(position), diff --git a/backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/service/AihrPracticeSeedServiceTest.java b/backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/service/AihrPracticeSeedServiceTest.java index 806022d5..4fac2370 100644 --- a/backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/service/AihrPracticeSeedServiceTest.java +++ b/backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/service/AihrPracticeSeedServiceTest.java @@ -201,6 +201,16 @@ public class AihrPracticeSeedServiceTest { assertTrue(migration.contains("repair-leak-followup")); } + @Test + public void scenarioUpsertKeepsAllValuePlaceholders() throws Exception { + String source = Files.readString(Path.of("src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java")); + int insertStart = source.indexOf("INSERT INTO aihr_practice_scenario"); + int updateStart = source.indexOf("ON DUPLICATE KEY UPDATE", insertStart); + String insertSql = source.substring(insertStart, updateStart); + + assertEquals(29, insertSql.chars().filter(value -> value == '?').count()); + } + @Test public void batchAssignmentUsesPerEmployeeTransactionsAndDatabaseIdempotency() throws Exception { String serviceSource = Files.readString(Path.of("src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java")); diff --git a/docs/DEV_SETUP.md b/docs/DEV_SETUP.md index 674ab4ca..86f73d27 100644 --- a/docs/DEV_SETUP.md +++ b/docs/DEV_SETUP.md @@ -130,6 +130,7 @@ AIHR_AI_SPEECH_ENABLED=true - `aihr_org_snapshot_mysql8.sql` 已纳入 reset 脚本;组织人员本地 seed(2 个住宅项目 22 人,项目经理/主管/一线三层)支撑演示,外部开放组织系统配置完成后用 `POST /api/aihr/org/sync` 拉取 `company/department/employee` 快照并覆盖本地 `aihr_org_snapshot`。2026-07-15 生产已完成该配置和覆盖同步,线上快照为 3417 名员工。 - 组织同步生产默认关闭 `aihr.org-sync.store-display-fields`,不把外部姓名/部门写入或返回组织人员展示快照;开发环境显式打开该开关仅用于 Demo。项目范围、岗位和外部主体 ID仍用于权限与身份映射。 - 本地组织 seed 带演示手机号;从 `aihr_org_snapshot` 按角色查询测试账号,分别验证员工端岗位识别和主管端项目范围,不在文档保存具体号码 +- 生产移动端回归同样由执行者从 `aihr_org_snapshot` 只读选择账号,不等待人工提供手机号:限定目标岗位、`employment_status='active'`、11 位 `person_phone`,并确认手机号与 `ext_party_id` 双向唯一。账号值只注入当前浏览器进程,不输出到终端、截图、报告或仓库。试点固定码开启时,登录顺序仍为先请求 `/resource/sms/code`、再提交固定码;直接提交固定码会得到 `Captcha invalid`,不应误判为账号或功能故障。 - SOP 知识库支持 `.txt/.md/.markdown/.pdf/.doc/.docx/.xls/.xlsx/.ppt/.pptx` 上传到 MinIO 后解析入库,接口为 `POST /api/knowledge/doc/upload`,单文件上限 100MB;管理端上传请求单独放宽到 180s,PDF 解析/归类/向量化较慢时不要改全局 axios 超时。 - 资料处理中心异步接口支持 `.zip` 和视频单文件 ≤500MB;生产 Spring/Undertow multipart 配置必须保持 `max-file-size=500MB`、`max-request-size=520MB`(源文件 `backend/ruoyi-admin/src/main/resources/application-prod.yml`,线上外置 `/opt/wygj/config/application-prod.yml`),修改后重启 `wygj-aihr.service`。非视频/ZIP 文件仍由队列限制为 100MB;ZIP 最多 1000 个子文件、解压总量 ≤2GB,不支持嵌套 ZIP。 - 视频(`.mp4/.mov/.avi/.mkv/.webm/.m4v`,≤500MB、≤60 分钟)只走资料处理中心批量导入(异步队列):ffmpeg 抽音轨分段调 asr 转写 + 抽关键帧调 vision 提取画面文字,合并后归类切片入库,片段带 `[mm:ss]` 时间戳;**依赖服务器安装 ffmpeg/ffprobe**(macOS `brew install ffmpeg`,Linux `apt install ffmpeg`)