From 0a30fe7034815f00285fa56179d31d3b4097ea70 Mon Sep 17 00:00:00 2001 From: let5sne Date: Tue, 14 Jul 2026 23:04:23 +0800 Subject: [PATCH] feat(aihr): align BRD scenarios and SOP seed content --- backend/script/sql/aihr_knowledge_mysql8.sql | 16 +++- backend/script/sql/aihr_practice_mysql8.sql | 10 ++- .../aihr_20260714_brd_sop_seed_mysql8.sql | 34 +++++++++ ...20260714_practice_brd_scenarios_mysql8.sql | 73 +++++++++++++++++++ docs/BRD_IMPLEMENTATION_AUDIT.md | 2 + scripts/demo-check.sh | 6 +- scripts/tests/aihr-knowledge-brd-sop.test.sh | 34 +++++++++ .../tests/aihr-practice-brd-scenarios.test.sh | 35 +++++++++ 8 files changed, 205 insertions(+), 5 deletions(-) create mode 100644 backend/script/sql/update/aihr_20260714_brd_sop_seed_mysql8.sql create mode 100644 backend/script/sql/update/aihr_20260714_practice_brd_scenarios_mysql8.sql create mode 100644 scripts/tests/aihr-knowledge-brd-sop.test.sh create mode 100644 scripts/tests/aihr-practice-brd-scenarios.test.sh diff --git a/backend/script/sql/aihr_knowledge_mysql8.sql b/backend/script/sql/aihr_knowledge_mysql8.sql index 25217ef4..4dfa9748 100644 --- a/backend/script/sql/aihr_knowledge_mysql8.sql +++ b/backend/script/sql/aihr_knowledge_mysql8.sql @@ -93,7 +93,9 @@ INSERT INTO `aihr_knowledge_info` VALUES (1001, '000000', '投诉处理 SOP', '业主投诉的接待、记录、派单、反馈和闭环确认。', '\n\n', 120, 5, 0.5, 800, NULL, NULL, 1, 1.0, '回答必须引用投诉处理 SOP 片段,不得编造未命中的金额、责任和时限。', NOW(), NOW(), 'seed:sop'), (1002, '000000', '催缴沟通 SOP', '缴费提醒、费用争议回应、服务问题承接。', '\n\n', 120, 5, 0.5, 800, NULL, NULL, 1, 1.0, '回答必须区分服务整改与费用义务。', NOW(), NOW(), 'seed:sop'), - (1003, '000000', '报修跟进 SOP', '报修接单、派工、现场反馈、复核关闭。', '\n\n', 120, 5, 0.5, 800, NULL, NULL, 1, 1.0, '回答必须给出明确节点和责任人。', NOW(), NOW(), 'seed:sop') + (1003, '000000', '报修跟进 SOP', '报修接单、派工、现场反馈、复核关闭。', '\n\n', 120, 5, 0.5, 800, NULL, NULL, 1, 1.0, '回答必须给出明确节点和责任人。', NOW(), NOW(), 'seed:sop'), + (1201, '000000', '增值服务需求识别 SOP', '基于业主真实需求介绍增值服务,明确适用范围、价格和取消规则。', '\n\n', 120, 5, 0.5, 800, NULL, NULL, 1, 1.0, '只能基于已确认的服务目录和价目口径回答,不得编造优惠或效果承诺。', NOW(), NOW(), 'seed:sop:brd-supplement'), + (1202, '000000', '日常服务回访 SOP', '服务完成后的满意度确认、遗留问题记录、责任人和回访节点。', '\n\n', 120, 5, 0.5, 800, NULL, NULL, 1, 1.0, '已完成不等于已闭环,回答必须包含满意度确认、责任人和下一次回访节点。', NOW(), NOW(), 'seed:sop:brd-supplement') ON DUPLICATE KEY UPDATE `description` = VALUES(`description`), `system_prompt` = VALUES(`system_prompt`), @@ -104,7 +106,9 @@ INSERT INTO `aihr_knowledge_attach` VALUES (1001, '000000', 1001, 'sop_complaint_v1', '投诉处理 SOP v1.0', 'sop', 2, NOW(), NOW(), 'owner:品质组'), (1002, '000000', 1002, 'sop_payment_v1', '催缴沟通 SOP v1.0', 'sop', 2, NOW(), NOW(), 'owner:财务组'), - (1003, '000000', 1003, 'sop_repair_v1', '报修跟进 SOP v1.0', 'sop', 2, NOW(), NOW(), 'owner:工程组') + (1003, '000000', 1003, 'sop_repair_v1', '报修跟进 SOP v1.0', 'sop', 2, NOW(), NOW(), 'owner:工程组'), + (1201, '000000', 1201, 'sop_service_promotion_v1', '增值服务需求识别 SOP v1.0', 'sop', 2, NOW(), NOW(), 'seed:sop:brd-supplement'), + (1202, '000000', 1202, 'sop_daily_service_v1', '日常服务回访 SOP v1.0', 'sop', 2, NOW(), NOW(), 'seed:sop:brd-supplement') ON DUPLICATE KEY UPDATE `status` = VALUES(`status`), `update_time` = NOW(); @@ -120,7 +124,13 @@ VALUES (100203, '000000', 1002, 3, 'sop_payment_v1', '对服务整改事项设置跟进责任人,并在约定时间内反馈处理进度。', NOW(), NOW(), 'seed:sop'), (100301, '000000', 1003, 1, 'sop_repair_v1', '报修超时跟进前,必须确认工单编号、责任班组、当前状态和超时原因。', NOW(), NOW(), 'seed:sop'), (100302, '000000', 1003, 2, 'sop_repair_v1', '向业主同步下一次反馈或到场时间,避免只说尽快处理。', NOW(), NOW(), 'seed:sop'), - (100303, '000000', 1003, 3, 'sop_repair_v1', '维修完成后需向业主确认处理效果,再补充关闭证据并关闭工单。', NOW(), NOW(), 'seed:sop') + (100303, '000000', 1003, 3, 'sop_repair_v1', '维修完成后需向业主确认处理效果,再补充关闭证据并关闭工单。', NOW(), NOW(), 'seed:sop'), + (120101, '000000', 1201, 1, 'sop_service_promotion_v1', '介绍增值服务前,先确认业主的家庭场景、实际需求、预算和期望结果,不直接罗列产品。', NOW(), NOW(), 'seed:sop:brd-supplement'), + (120102, '000000', 1201, 2, 'sop_service_promotion_v1', '服务介绍必须说明适用范围、正式价目、办理路径和取消规则;未确认的信息不得口头承诺。', NOW(), NOW(), 'seed:sop:brd-supplement'), + (120103, '000000', 1201, 3, 'sop_service_promotion_v1', '业主暂不需要时应记录需求和后续咨询方式,不以夸大效果、强行推销或虚构优惠促成转化。', NOW(), NOW(), 'seed:sop:brd-supplement'), + (120201, '000000', 1202, 1, 'sop_daily_service_v1', '服务完成后要确认业主是否满意、是否还有遗留问题,并记录新的服务需求。', NOW(), NOW(), 'seed:sop:brd-supplement'), + (120202, '000000', 1202, 2, 'sop_daily_service_v1', '对未解决事项明确责任人、下一次反馈时间和记录方式,不能只说已经处理。', NOW(), NOW(), 'seed:sop:brd-supplement'), + (120203, '000000', 1202, 3, 'sop_daily_service_v1', '回访结束后更新服务记录;业主再次反馈时,应能沿用既有记录继续处理,避免重复询问。', NOW(), NOW(), 'seed:sop:brd-supplement') ON DUPLICATE KEY UPDATE `content` = VALUES(`content`), `update_time` = NOW(); diff --git a/backend/script/sql/aihr_practice_mysql8.sql b/backend/script/sql/aihr_practice_mysql8.sql index b2558d7f..a40e05c7 100644 --- a/backend/script/sql/aihr_practice_mysql8.sql +++ b/backend/script/sql/aihr_practice_mysql8.sql @@ -341,7 +341,15 @@ VALUES ('000000', 'emergency-security', '陌生人尾随求助', '生活顾问', '突发求助', '住宅', 3, '夜间回家、感到被尾随、需要安全协助的业主', '我刚进小区感觉有人跟着我,你们保安在哪里?', '安全求助', '希望马上获得陪同和安全确认', '要求业主自己确认、轻描淡写、泄露巡逻盲区', '先确认位置、安排安保、保持通话或引导到安全点', '安全巡逻SOP;突发求助SOP', '确认实时位置,安排安保接应,引导到明亮公共区域并保留记录', '提示不要透露安防盲区', '训练夜间安全求助的接警式沟通。', '张三', 54, 82, - '[{"customer":"我刚进小区感觉有人跟着我,你们保安在哪里?","coachHint":"先确认当前位置和是否安全,安排安保接应。","emotion":86,"trust":62},{"customer":"我要不要直接回家?","coachHint":"引导到明亮公共区域或岗亭,保持联系。","emotion":76,"trust":82}]', 1, 120, NOW(), NOW()) + '[{"customer":"我刚进小区感觉有人跟着我,你们保安在哪里?","coachHint":"先确认当前位置和是否安全,安排安保接应。","emotion":86,"trust":62},{"customer":"我要不要直接回家?","coachHint":"引导到明亮公共区域或岗亭,保持联系。","emotion":76,"trust":82}]', 1, 120, NOW(), NOW()), +('000000', 'service-promotion-needs', '增值服务需求识别与推介', '生活顾问', '服务推介/增值', '住宅', 2, + '关注生活品质、但对营销话术敏感的业主', '最近家里老人要长期出门,社区有没有更方便的生活服务?', '识别真实需求并介绍适配的增值服务', '希望得到有用建议而不是被强行推销', '不问需求就罗列产品、夸大承诺、把推介当成收费催促', '先确认家庭场景和预算,再介绍适配服务并说明价格与取消规则', '增值服务推介SOP;客户需求识别SOP', + '先提问确认需求,再说明服务适用范围、价格口径和后续咨询路径', '提示不得编造优惠、效果或未确认的服务承诺', '训练生活顾问在真实需求基础上完成合规的增值服务介绍。', '张三', 63, 84, + '[{"customer":"我不喜欢被推销,你先说这个服务到底能解决什么问题。","coachHint":"先复述业主需求,再说明适用场景,不要直接报一串产品。","emotion":68,"trust":72},{"customer":"那具体多少钱?可以随时取消吗?","coachHint":"金额和取消规则必须以正式价目与合同口径为准,不能现场编造。","emotion":58,"trust":84}]', 1, 130, NOW(), NOW()), +('000000', 'daily-service-followup', '日常服务主动回访', '生活顾问', '日常服务', '住宅', 1, + '刚办理过服务、希望有人持续跟进的业主', '上次你们帮我处理完了,我想知道后面还会有人跟进吗?', '完成日常服务回访与问题闭环', '希望被持续关注,避免问题处理后无人回应', '只说已经完成、不给回访节点、不记录新的服务需求', '确认结果是否满足、记录新问题、约定回访节点并留下服务记录', '日常服务回访SOP;服务闭环SOP', + '确认满意度和遗留问题,明确责任人、回访时间和记录方式', '提示已完成不等于已闭环,必须确认业主感受和后续动作', '训练生活顾问把一次性服务延伸为有记录、有节点的日常回访。', '张三', 66, 85, + '[{"customer":"事情是处理了,但我不知道以后有问题找谁。","coachHint":"确认当前结果和遗留问题,给出明确联系人和回访节点。","emotion":62,"trust":74},{"customer":"那你们会记下来吗?别下次又要从头说。","coachHint":"说明服务记录和后续跟进方式,避免只做口头承诺。","emotion":54,"trust":85}]', 1, 140, NOW(), NOW()) ON DUPLICATE KEY UPDATE `scenario_name` = VALUES(`scenario_name`), `position` = VALUES(`position`), diff --git a/backend/script/sql/update/aihr_20260714_brd_sop_seed_mysql8.sql b/backend/script/sql/update/aihr_20260714_brd_sop_seed_mysql8.sql new file mode 100644 index 00000000..fe910757 --- /dev/null +++ b/backend/script/sql/update/aihr_20260714_brd_sop_seed_mysql8.sql @@ -0,0 +1,34 @@ +-- AIHR BRD 住宅 SOP 补充迁移(MySQL 8.x) +-- 补齐服务推介与日常服务两类可检索 SOP;不写入正式评审数据,可重复执行。 + +INSERT INTO `aihr_knowledge_info` + (`id`, `tenant_id`, `name`, `description`, `separator`, `overlap_char`, `retrieve_limit`, `similarity_threshold`, `text_block_size`, `vector_model`, `embedding_model`, `enable_hybrid`, `hybrid_alpha`, `system_prompt`, `create_time`, `update_time`, `remark`) +VALUES + (1201, '000000', '增值服务需求识别 SOP', '基于业主真实需求介绍增值服务,明确适用范围、价格和取消规则。', '\n\n', 120, 5, 0.5, 800, NULL, NULL, 1, 1.0, '只能基于已确认的服务目录和价目口径回答,不得编造优惠或效果承诺。', NOW(), NOW(), 'seed:sop:brd-supplement'), + (1202, '000000', '日常服务回访 SOP', '服务完成后的满意度确认、遗留问题记录、责任人和回访节点。', '\n\n', 120, 5, 0.5, 800, NULL, NULL, 1, 1.0, '已完成不等于已闭环,回答必须包含满意度确认、责任人和下一次回访节点。', NOW(), NOW(), 'seed:sop:brd-supplement') +ON DUPLICATE KEY UPDATE + `description` = VALUES(`description`), + `system_prompt` = VALUES(`system_prompt`), + `update_time` = NOW(); + +INSERT INTO `aihr_knowledge_attach` + (`id`, `tenant_id`, `knowledge_id`, `doc_id`, `name`, `type`, `status`, `create_time`, `update_time`, `remark`) +VALUES + (1201, '000000', 1201, 'sop_service_promotion_v1', '增值服务需求识别 SOP v1.0', 'sop', 2, NOW(), NOW(), 'seed:sop:brd-supplement'), + (1202, '000000', 1202, 'sop_daily_service_v1', '日常服务回访 SOP v1.0', 'sop', 2, NOW(), NOW(), 'seed:sop:brd-supplement') +ON DUPLICATE KEY UPDATE + `status` = VALUES(`status`), + `update_time` = NOW(); + +INSERT INTO `aihr_knowledge_fragment` + (`id`, `tenant_id`, `knowledge_id`, `idx`, `doc_id`, `content`, `create_time`, `update_time`, `remark`) +VALUES + (120101, '000000', 1201, 1, 'sop_service_promotion_v1', '介绍增值服务前,先确认业主的家庭场景、实际需求、预算和期望结果,不直接罗列产品。', NOW(), NOW(), 'seed:sop:brd-supplement'), + (120102, '000000', 1201, 2, 'sop_service_promotion_v1', '服务介绍必须说明适用范围、正式价目、办理路径和取消规则;未确认的信息不得口头承诺。', NOW(), NOW(), 'seed:sop:brd-supplement'), + (120103, '000000', 1201, 3, 'sop_service_promotion_v1', '业主暂不需要时应记录需求和后续咨询方式,不以夸大效果、强行推销或虚构优惠促成转化。', NOW(), NOW(), 'seed:sop:brd-supplement'), + (120201, '000000', 1202, 1, 'sop_daily_service_v1', '服务完成后要确认业主是否满意、是否还有遗留问题,并记录新的服务需求。', NOW(), NOW(), 'seed:sop:brd-supplement'), + (120202, '000000', 1202, 2, 'sop_daily_service_v1', '对未解决事项明确责任人、下一次反馈时间和记录方式,不能只说已经处理。', NOW(), NOW(), 'seed:sop:brd-supplement'), + (120203, '000000', 1202, 3, 'sop_daily_service_v1', '回访结束后更新服务记录;业主再次反馈时,应能沿用既有记录继续处理,避免重复询问。', NOW(), NOW(), 'seed:sop:brd-supplement') +ON DUPLICATE KEY UPDATE + `content` = VALUES(`content`), + `update_time` = NOW(); diff --git a/backend/script/sql/update/aihr_20260714_practice_brd_scenarios_mysql8.sql b/backend/script/sql/update/aihr_20260714_practice_brd_scenarios_mysql8.sql new file mode 100644 index 00000000..95a91bb2 --- /dev/null +++ b/backend/script/sql/update/aihr_20260714_practice_brd_scenarios_mysql8.sql @@ -0,0 +1,73 @@ +-- AIHR BRD 补充场景迁移(MySQL 8.x) +-- 补齐 BRD 4.3.4 的服务推介与日常服务场景;不写入正式试点训练、校准或评审数据,可重复执行。 + +INSERT INTO `aihr_practice_scenario` + (`tenant_id`, `scenario_code`, `scenario_name`, `position`, `scenario_type`, `project_type`, `difficulty`, + `customer_persona`, `opening_text`, `explicit_need`, `hidden_need`, `irritants`, `delights`, `sop_refs`, + `success_criteria`, `strategy`, `goal`, `trainee_name`, `trust_start`, `trust_end`, `rounds_json`, `enabled`, `sort_order`, `create_time`, `update_time`) +VALUES +('000000', 'service-promotion-needs', '增值服务需求识别与推介', '生活顾问', '服务推介/增值', '住宅', 2, + '关注生活品质、但对营销话术敏感的业主', '最近家里老人要长期出门,社区有没有更方便的生活服务?', '识别真实需求并介绍适配的增值服务', '希望得到有用建议而不是被强行推销', '不问需求就罗列产品、夸大承诺、把推介当成收费催促', '先确认家庭场景和预算,再介绍适配服务并说明价格与取消规则', '增值服务推介SOP;客户需求识别SOP', + '先提问确认需求,再说明服务适用范围、价格口径和后续咨询路径', '提示不得编造优惠、效果或未确认的服务承诺', '训练生活顾问在真实需求基础上完成合规的增值服务介绍。', '张三', 63, 84, + '[{"customer":"我不喜欢被推销,你先说这个服务到底能解决什么问题。","coachHint":"先复述业主需求,再说明适用场景,不要直接报一串产品。","emotion":68,"trust":72},{"customer":"那具体多少钱?可以随时取消吗?","coachHint":"金额和取消规则必须以正式价目与合同口径为准,不能现场编造。","emotion":58,"trust":84}]', 1, 130, NOW(), NOW()), +('000000', 'daily-service-followup', '日常服务主动回访', '生活顾问', '日常服务', '住宅', 1, + '刚办理过服务、希望有人持续跟进的业主', '上次你们帮我处理完了,我想知道后面还会有人跟进吗?', '完成日常服务回访与问题闭环', '希望被持续关注,避免问题处理后无人回应', '只说已经完成、不给回访节点、不记录新的服务需求', '确认结果是否满足、记录新问题、约定回访节点并留下服务记录', '日常服务回访SOP;服务闭环SOP', + '确认满意度和遗留问题,明确责任人、回访时间和记录方式', '提示已完成不等于已闭环,必须确认业主感受和后续动作', '训练生活顾问把一次性服务延伸为有记录、有节点的日常回访。', '张三', 66, 85, + '[{"customer":"事情是处理了,但我不知道以后有问题找谁。","coachHint":"确认当前结果和遗留问题,给出明确联系人和回访节点。","emotion":62,"trust":74},{"customer":"那你们会记下来吗?别下次又要从头说。","coachHint":"说明服务记录和后续跟进方式,避免只做口头承诺。","emotion":54,"trust":85}]', 1, 140, NOW(), NOW()) +ON DUPLICATE KEY UPDATE + `scenario_name` = VALUES(`scenario_name`), + `position` = VALUES(`position`), + `scenario_type` = VALUES(`scenario_type`), + `project_type` = VALUES(`project_type`), + `difficulty` = VALUES(`difficulty`), + `customer_persona` = VALUES(`customer_persona`), + `opening_text` = VALUES(`opening_text`), + `explicit_need` = VALUES(`explicit_need`), + `hidden_need` = VALUES(`hidden_need`), + `irritants` = VALUES(`irritants`), + `delights` = VALUES(`delights`), + `sop_refs` = VALUES(`sop_refs`), + `success_criteria` = VALUES(`success_criteria`), + `strategy` = VALUES(`strategy`), + `goal` = VALUES(`goal`), + `trainee_name` = VALUES(`trainee_name`), + `trust_start` = VALUES(`trust_start`), + `trust_end` = VALUES(`trust_end`), + `rounds_json` = VALUES(`rounds_json`), + `enabled` = VALUES(`enabled`), + `sort_order` = VALUES(`sort_order`), + `update_time` = VALUES(`update_time`); + +INSERT INTO `aihr_practice_rubric` + (`tenant_id`, `rubric_code`, `scenario_code`, `rubric_name`, `version`, `enabled`, `description`, `create_time`, `update_time`) +SELECT `tenant_id`, CONCAT('rubric-', `scenario_code`), `scenario_code`, CONCAT(`scenario_name`, ' Rubric'), + 'v1', 1, '生活顾问试点五维评分标准', NOW(), NOW() +FROM `aihr_practice_scenario` +WHERE `tenant_id` = '000000' + AND `scenario_code` IN ('service-promotion-needs', 'daily-service-followup') +ON DUPLICATE KEY UPDATE + `rubric_name` = VALUES(`rubric_name`), + `scenario_code` = VALUES(`scenario_code`), + `enabled` = VALUES(`enabled`), + `description` = VALUES(`description`), + `update_time` = VALUES(`update_time`); + +INSERT INTO `aihr_practice_rubric_dimension` + (`tenant_id`, `rubric_code`, `dimension_code`, `dimension_name`, `weight`, `description`, `sort_order`, `create_time`, `update_time`) +SELECT r.`tenant_id`, r.`rubric_code`, d.`dimension_code`, d.`dimension_name`, d.`weight`, d.`description`, d.`sort_order`, NOW(), NOW() +FROM `aihr_practice_rubric` r +JOIN ( + SELECT 'task_completion' AS `dimension_code`, '任务完成度' AS `dimension_name`, 0.40 AS `weight`, '问题解决有效性、服务闭环率、SOP关键点执行率' AS `description`, 10 AS `sort_order` + UNION ALL SELECT 'standardization', '话术规范性', 0.25, '流程完整性、沟通合规、用语专业度', 20 + UNION ALL SELECT 'emotion', '情绪管理能力', 0.20, '共情、语速语气控制、业主情绪引导安抚有效性', 30 + UNION ALL SELECT 'response_timeliness', '响应时效', 0.10, '首响时长、处理周期、跟进频率', 40 + UNION ALL SELECT 'marketing', '增值转化潜力', 0.05, '需求识别、服务介绍时机与合规表达', 50 +) d +WHERE r.`tenant_id` = '000000' + AND r.`scenario_code` IN ('service-promotion-needs', 'daily-service-followup') +ON DUPLICATE KEY UPDATE + `dimension_name` = VALUES(`dimension_name`), + `weight` = VALUES(`weight`), + `description` = VALUES(`description`), + `sort_order` = VALUES(`sort_order`), + `update_time` = VALUES(`update_time`); diff --git a/docs/BRD_IMPLEMENTATION_AUDIT.md b/docs/BRD_IMPLEMENTATION_AUDIT.md index 7d52e8cd..b557ca2e 100644 --- a/docs/BRD_IMPLEMENTATION_AUDIT.md +++ b/docs/BRD_IMPLEMENTATION_AUDIT.md @@ -361,3 +361,5 @@ - 2026-07-14 BRD G3 Qdrant 共享 collection 隔离修复:重建单租户向量索引此前会删除整个共享 Qdrant collection,并用全局点数与当前租户 MySQL 数量比较;现改为按当前租户 payload filter 删除和统计点数,保留其他租户向量。新增回归契约与 `demo-check` marker;未修改业务数据或生产环境。 - 2026-07-14 BRD G3 AI 面试内存会话租户隔离修复:候选人面试进行中的内存会话此前只按 `sessionId` 全局缓存,系统用户在已知会话 ID 时可能把其他租户会话带入当前租户完成并落库,候选人存在性判断也可能跨租户命中;现为内存会话保存创建租户,在回答、完成和候选人存在性判断时校验当前租户,并补源码回归断言与 `demo-check` marker;未修改业务数据或生产环境。 - 2026-07-14 BRD G3 录音资源租户归属显式校验:系统用户读取训练录音此前仅检查 `superadmin/hr_operator` 角色,随后依赖通用 OSS 服务的隐式租户过滤;现额外要求 `aihr_practice_audio` 在当前租户存在对应 `oss_id`,再进入通用下载服务,避免跨租户对象 ID 被误读。新增服务路径、控制器回归断言与 `demo-check` marker;未修改业务数据或生产环境。 +- 2026-07-14 BRD 4.3.4 场景覆盖补齐:原生活顾问 12 条 seed 只有投诉、催费、报修、突发求助,缺少 BRD 明确要求的服务推介/增值与日常服务;现新增 `service-promotion-needs`、`daily-service-followup` 两条补充场景及五维 Rubric,保留原 12 条核心场景不变。提供幂等迁移 `aihr_20260714_practice_brd_scenarios_mysql8.sql` 和临时库回归脚本;该内容属于待业务复核的初始化素材,不等同于正式试点样本。 +- 2026-07-14 BRD 4.7 住宅 SOP 内容补齐:原知识库只有 3 份已解析住宅 SOP;现新增增值服务需求识别、日常服务回访两份 SOP 及各 3 个检索片段,reset SQL 与版本化迁移同步,临时库连续执行两次保持幂等。严格门禁已从 `sop_docs=3/5` 推进到 `5/5`;案例库、正式组织身份和正式试点证据仍未完成。 diff --git a/scripts/demo-check.sh b/scripts/demo-check.sh index 2842abf3..e13fece9 100755 --- a/scripts/demo-check.sh +++ b/scripts/demo-check.sh @@ -497,7 +497,7 @@ contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service contains frontend/src/api/aihr/practice.ts "/api/train/practice/start" contains frontend/src/api/aihr/practice.ts "/api/aihr/prompt-templates" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrPracticeController.java "/api/train/practice" -contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrPracticeController.java "practiceSeedService.turn(request, currentAppUsername())" +contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrPracticeController.java "practiceSeedService.turn(request, currentAppUsername(), currentAppUserId())" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrPracticeController.java "practiceSeedService.finish(request, currentAppUsername())" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java "训练会话不存在或无权访问" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java "finished_time IS NOT NULL" @@ -529,6 +529,10 @@ contains mobile-uni/src/pages/user/learning/index.vue "mistakeFilters" contains mobile-uni/src/pages/user/profile/index.vue "AI训练综合分仅作训练参考" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java "aihr_practice_session" contains backend/script/sql/aihr_practice_mysql8.sql "aihr_practice_scenario" +contains backend/script/sql/update/aihr_20260714_practice_brd_scenarios_mysql8.sql "service-promotion-needs" +contains backend/script/sql/update/aihr_20260714_practice_brd_scenarios_mysql8.sql "daily-service-followup" +contains backend/script/sql/update/aihr_20260714_brd_sop_seed_mysql8.sql "sop_service_promotion_v1" +contains backend/script/sql/update/aihr_20260714_brd_sop_seed_mysql8.sql "sop_daily_service_v1" contains backend/script/sql/aihr_practice_mysql8.sql "aihr_prompt_template" contains backend/script/sql/aihr_practice_mysql8.sql "summary_card" contains frontend/src/views/train/practice.vue "训练前预习卡" diff --git a/scripts/tests/aihr-knowledge-brd-sop.test.sh b/scripts/tests/aihr-knowledge-brd-sop.test.sh new file mode 100644 index 00000000..ec3ab94b --- /dev/null +++ b/scripts/tests/aihr-knowledge-brd-sop.test.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -euo pipefail + +if ! command -v docker >/dev/null || ! docker ps --format '{{.Names}}' | grep -qx 'wygj-mysql'; then + echo "SKIP: AIHR BRD SOP migration test requires wygj-mysql" + exit 0 +fi + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +MYSQL_CONTAINER="wygj-mysql" +DB="wygj_aihr_brd_sop_test_$$" + +mysql() { + docker exec -i "$MYSQL_CONTAINER" mysql -uroot -proot --default-character-set=utf8mb4 "$@" +} + +cleanup() { + mysql -e "DROP DATABASE IF EXISTS \`$DB\`;" >/dev/null 2>&1 || true +} +trap cleanup EXIT + +mysql -e "CREATE DATABASE \`$DB\` CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;" +mysql "$DB" < "$ROOT_DIR/backend/script/sql/aihr_knowledge_mysql8.sql" +mysql "$DB" < "$ROOT_DIR/backend/script/sql/update/aihr_20260714_brd_sop_seed_mysql8.sql" +mysql "$DB" < "$ROOT_DIR/backend/script/sql/update/aihr_20260714_brd_sop_seed_mysql8.sql" + +counts="$(mysql "$DB" -N -B -e " + SELECT + (SELECT COUNT(*) FROM aihr_knowledge_attach WHERE type='sop' AND status=2), + (SELECT COUNT(*) FROM aihr_knowledge_fragment WHERE doc_id IN ('sop_service_promotion_v1', 'sop_daily_service_v1')); +")" +test "$counts" = $'5\t6' + +echo "PASS: AIHR BRD SOP migration is idempotent" diff --git a/scripts/tests/aihr-practice-brd-scenarios.test.sh b/scripts/tests/aihr-practice-brd-scenarios.test.sh new file mode 100644 index 00000000..949a16da --- /dev/null +++ b/scripts/tests/aihr-practice-brd-scenarios.test.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +set -euo pipefail + +if ! command -v docker >/dev/null || ! docker ps --format '{{.Names}}' | grep -qx 'wygj-mysql'; then + echo "SKIP: AIHR BRD scenario migration test requires wygj-mysql" + exit 0 +fi + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +MYSQL_CONTAINER="wygj-mysql" +DB="wygj_aihr_brd_scenario_test_$$" + +mysql() { + docker exec -i "$MYSQL_CONTAINER" mysql -uroot -proot --default-character-set=utf8mb4 "$@" +} + +cleanup() { + mysql -e "DROP DATABASE IF EXISTS \`$DB\`;" >/dev/null 2>&1 || true +} +trap cleanup EXIT + +mysql -e "CREATE DATABASE \`$DB\` CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;" +mysql "$DB" < "$ROOT_DIR/backend/script/sql/aihr_practice_mysql8.sql" +mysql "$DB" < "$ROOT_DIR/backend/script/sql/update/aihr_20260714_practice_brd_scenarios_mysql8.sql" +mysql "$DB" < "$ROOT_DIR/backend/script/sql/update/aihr_20260714_practice_brd_scenarios_mysql8.sql" + +counts="$(mysql "$DB" -N -B -e " + SELECT + (SELECT COUNT(*) FROM aihr_practice_scenario WHERE scenario_code IN ('service-promotion-needs', 'daily-service-followup')), + (SELECT COUNT(*) FROM aihr_practice_rubric WHERE scenario_code IN ('service-promotion-needs', 'daily-service-followup')), + (SELECT COUNT(*) FROM aihr_practice_rubric_dimension WHERE rubric_code IN ('rubric-service-promotion-needs', 'rubric-daily-service-followup')); +")" +test "$counts" = $'2\t2\t10' + +echo "PASS: AIHR BRD practice scenarios migration is idempotent"