#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" FRONTEND_URL="${FRONTEND_URL:-https://wygj-admin.localhost}" MOBILE_URL="${MOBILE_URL:-https://wygj-mobile-uni.localhost}" cd "$ROOT_DIR" fail() { echo "FAIL: $*" >&2 exit 1 } need() { command -v "$1" >/dev/null || fail "missing command: $1" } contains() { local file="$1" local text="$2" grep -Fq "$text" "$file" || fail "$file missing marker: $text" } check_url() { local name="$1" local url="$2" local code code="$(curl -k -sS -o /tmp/wygj-demo-check.body -w '%{http_code}' "$url")" [[ "$code" == "200" ]] || fail "$name returned HTTP $code" echo "OK: $name" } check_mobile_h5_shell() { local html href icon_url code html="$(curl -k -fsS "$MOBILE_URL/h5/")" [[ "$html" != *"tongji-collector"* && "$html" != *"WebTrack.gif"* && "$html" != *"uni-stat"* ]] || fail "mobile H5 includes uni statistics runtime" [[ "$html" != *"/h5/h5/"* ]] || fail "mobile H5 has duplicated /h5 base path" href="$(printf '%s' "$html" | sed -nE 's/.*/dev/null } check_pilot_samples() { if ! command -v docker >/dev/null || ! docker ps --format '{{.Names}}' | grep -qx 'wygj-mysql'; then [[ "${AIHR_PILOT_STRICT:-false}" != "true" ]] || fail "pilot DB sample counts unavailable (wygj-mysql not running)" echo "SKIP: pilot DB sample counts (wygj-mysql not running)" return fi if ! pilot_sql_scalar "SELECT 1" >/dev/null; then [[ "${AIHR_PILOT_STRICT:-false}" != "true" ]] || fail "pilot DB sample counts unavailable (database connection failed)" echo "SKIP: pilot DB sample counts (database connection failed)" return fi if [[ "${AIHR_PILOT_STRICT:-false}" == "true" ]] && { [[ -z "${AIHR_PILOT_START_DATE:-}" ]] || [[ -z "${AIHR_PILOT_END_DATE:-}" ]] }; then fail "strict pilot check requires AIHR_PILOT_START_DATE and AIHR_PILOT_END_DATE" fi local pilot_start="${AIHR_PILOT_START_DATE:-$(date +%F)}" local pilot_end="${AIHR_PILOT_END_DATE:-$pilot_start}" local pilot_tenant="${AIHR_PILOT_TENANT_ID:-000000}" [[ "$pilot_tenant" =~ ^[A-Za-z0-9_-]+$ ]] || fail "invalid AIHR_PILOT_TENANT_ID: $pilot_tenant" [[ "$pilot_start" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] || fail "invalid AIHR_PILOT_START_DATE: $pilot_start" [[ "$pilot_end" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] || fail "invalid AIHR_PILOT_END_DATE: $pilot_end" [[ "$(pilot_sql_scalar "SELECT DATE('$pilot_start') IS NOT NULL")" == "1" ]] || fail "invalid AIHR_PILOT_START_DATE: $pilot_start" [[ "$(pilot_sql_scalar "SELECT DATE('$pilot_end') IS NOT NULL")" == "1" ]] || fail "invalid AIHR_PILOT_END_DATE: $pilot_end" [[ "$pilot_start" > "$pilot_end" ]] && fail "pilot start date must not be after end date" local scenario_count scenario_service_promotion_count scenario_daily_service_count completed_people pilot_people org_active org_phone_linked org_pilot_people org_pilot_projects org_completed_people audio_table_count prompt_version_schema_count sop_document_count case_stored_count case_unmasked_count local calibration_stats calibration_count calibration_matched sop_total sop_usable sop_pending satisfaction_count satisfaction_average local org_identity_cte=" WITH active_org AS ( SELECT DISTINCT ext_party_id, project_code, person_phone FROM aihr_org_snapshot WHERE tenant_id = '$pilot_tenant' AND employment_status = 'active' AND person_phone IS NOT NULL AND person_phone <> '' ), all_org_keys AS ( SELECT ext_party_id AS identity_key, ext_party_id FROM active_org WHERE ext_party_id IS NOT NULL AND ext_party_id <> '' UNION ALL SELECT person_phone AS identity_key, ext_party_id FROM active_org ), conflict_keys AS ( SELECT identity_key FROM all_org_keys GROUP BY identity_key HAVING COUNT(DISTINCT ext_party_id) > 1 ), unsafe_org AS ( SELECT DISTINCT k.ext_party_id FROM all_org_keys k JOIN conflict_keys c ON c.identity_key = k.identity_key ), safe_org AS ( SELECT o.ext_party_id, o.project_code, o.person_phone FROM active_org o LEFT JOIN unsafe_org u ON u.ext_party_id = o.ext_party_id WHERE u.ext_party_id IS NULL ), org_key_candidates AS ( SELECT ext_party_id AS identity_key, ext_party_id FROM safe_org WHERE ext_party_id IS NOT NULL AND ext_party_id <> '' UNION ALL SELECT person_phone AS identity_key, ext_party_id FROM safe_org ), org_identity AS ( SELECT identity_key, MIN(ext_party_id) AS ext_party_id FROM org_key_candidates GROUP BY identity_key HAVING COUNT(DISTINCT ext_party_id) = 1 )" scenario_count="$(pilot_sql_scalar "SELECT COUNT(*) FROM aihr_practice_scenario WHERE tenant_id = '$pilot_tenant' AND position = '生活顾问' AND enabled = 1")" # ponytail: presence-only core-flow gate; content quality and SOP applicability still require HR review. scenario_service_promotion_count="$(pilot_sql_scalar "SELECT COUNT(*) FROM aihr_practice_scenario WHERE tenant_id = '$pilot_tenant' AND position = '生活顾问' AND enabled = 1 AND (scenario_type LIKE '%推介%' OR scenario_type LIKE '%增值%' OR scenario_name LIKE '%推介%' OR scenario_name LIKE '%增值%')")" scenario_daily_service_count="$(pilot_sql_scalar "SELECT COUNT(*) FROM aihr_practice_scenario WHERE tenant_id = '$pilot_tenant' AND position = '生活顾问' AND enabled = 1 AND (scenario_type LIKE '%日常%' OR scenario_name LIKE '%日常%')")" audio_table_count="$(pilot_sql_scalar "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = 'aihr_practice_audio'")" prompt_version_schema_count="$(pilot_sql_scalar "SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'aihr_sop_answer_review' AND column_name = 'prompt_version'")" sop_document_count="$(pilot_sql_scalar "SELECT COUNT(*) FROM aihr_knowledge_attach WHERE tenant_id = '$pilot_tenant' AND type = 'sop' AND status = 2")" case_stored_count="$(pilot_sql_scalar "SELECT COUNT(*) FROM aihr_case_record WHERE tenant_id = '$pilot_tenant' AND status = '已入库'")" case_unmasked_count="$(pilot_sql_scalar "SELECT COUNT(*) FROM aihr_case_record WHERE tenant_id = '$pilot_tenant' AND status = '已入库' AND CONCAT_WS(' ', title, transcript, summary) REGEXP '1[3-9][0-9]{9}|[[:alnum:]._%+-]+@[[:alnum:].-]+\\.[[:alpha:]]{2,}|[0-9]{1,2}(栋|幢|号楼|单元)[0-9]{3,4}|[0-9]{1,2}[--][0-9]{3,4}'")" completed_people="$(pilot_sql_scalar " SELECT COUNT(*) FROM ( SELECT ext_party_id FROM aihr_practice_session WHERE tenant_id = '$pilot_tenant' AND mode = 'mobile' AND finished_time >= '${pilot_start} 00:00:00' AND finished_time < DATE_ADD('${pilot_end}', INTERVAL 1 DAY) GROUP BY ext_party_id HAVING COUNT(*) >= 10 ) completed_people")" pilot_people="$(pilot_sql_scalar " SELECT COUNT(*) FROM ( SELECT ext_party_id FROM aihr_practice_session WHERE tenant_id = '$pilot_tenant' AND mode = 'mobile' AND finished_time >= '${pilot_start} 00:00:00' AND finished_time < DATE_ADD('${pilot_end}', INTERVAL 1 DAY) UNION SELECT ext_party_id FROM aihr_practice_assignment WHERE tenant_id = '$pilot_tenant' AND create_time >= '${pilot_start} 00:00:00' AND create_time < DATE_ADD('${pilot_end}', INTERVAL 1 DAY) ) pilot_people")" org_active="$(pilot_sql_scalar "SELECT COUNT(*) FROM aihr_org_snapshot WHERE tenant_id = '$pilot_tenant' AND employment_status = 'active'")" org_phone_linked="$(pilot_sql_scalar "$org_identity_cte SELECT COUNT(DISTINCT ext_party_id) FROM safe_org")" org_pilot_people="$(pilot_sql_scalar " $org_identity_cte, pilot_identity AS ( SELECT ext_party_id identity_key FROM aihr_practice_session WHERE tenant_id = '$pilot_tenant' AND mode = 'mobile' AND finished_time >= '${pilot_start} 00:00:00' AND finished_time < DATE_ADD('${pilot_end}', INTERVAL 1 DAY) UNION SELECT ext_party_id identity_key FROM aihr_practice_assignment WHERE tenant_id = '$pilot_tenant' AND create_time >= '${pilot_start} 00:00:00' AND create_time < DATE_ADD('${pilot_end}', INTERVAL 1 DAY) ) SELECT COUNT(DISTINCT o.ext_party_id) FROM pilot_identity p JOIN org_identity o ON o.identity_key = p.identity_key")" org_pilot_projects="$(pilot_sql_scalar " $org_identity_cte, pilot_identity AS ( SELECT ext_party_id identity_key FROM aihr_practice_session WHERE tenant_id = '$pilot_tenant' AND mode = 'mobile' AND finished_time >= '${pilot_start} 00:00:00' AND finished_time < DATE_ADD('${pilot_end}', INTERVAL 1 DAY) UNION SELECT ext_party_id identity_key FROM aihr_practice_assignment WHERE tenant_id = '$pilot_tenant' AND create_time >= '${pilot_start} 00:00:00' AND create_time < DATE_ADD('${pilot_end}', INTERVAL 1 DAY) ), pilot_org AS ( SELECT DISTINCT o.ext_party_id FROM pilot_identity p JOIN org_identity o ON o.identity_key = p.identity_key ) SELECT COUNT(DISTINCT o.project_code) FROM pilot_org p JOIN safe_org o ON o.ext_party_id = p.ext_party_id WHERE o.project_code IS NOT NULL AND o.project_code <> ''")" org_completed_people="$(pilot_sql_scalar " $org_identity_cte, session_identity AS ( SELECT ext_party_id AS identity_key, COUNT(*) AS session_count FROM aihr_practice_session WHERE tenant_id = '$pilot_tenant' AND mode = 'mobile' AND finished_time >= '${pilot_start} 00:00:00' AND finished_time < DATE_ADD('${pilot_end}', INTERVAL 1 DAY) GROUP BY ext_party_id ), completed_org AS ( SELECT o.ext_party_id FROM session_identity s JOIN org_identity o ON o.identity_key = s.identity_key GROUP BY o.ext_party_id HAVING SUM(s.session_count) >= 10 ) SELECT COUNT(*) FROM completed_org")" calibration_stats="$(pilot_sql_scalar " $org_identity_cte, formal_sessions AS ( SELECT s.session_id FROM aihr_practice_session s JOIN org_identity o ON o.identity_key = s.ext_party_id WHERE s.tenant_id = '$pilot_tenant' AND s.mode = 'mobile' AND s.finished_time >= '${pilot_start} 00:00:00' AND s.finished_time < DATE_ADD('${pilot_end}', INTERVAL 1 DAY) ), latest_calibration AS ( SELECT c.session_id, MAX(c.id) AS id FROM aihr_practice_calibration c JOIN formal_sessions s ON s.session_id = c.session_id WHERE c.tenant_id = '$pilot_tenant' AND c.original_score IS NOT NULL AND c.corrected_score IS NOT NULL AND c.create_time >= '${pilot_start} 00:00:00' AND c.create_time < DATE_ADD('${pilot_end}', INTERVAL 1 DAY) GROUP BY c.session_id ) SELECT COUNT(*) AS total, COALESCE(SUM(CASE WHEN (CASE WHEN c.original_score < 60 THEN 0 WHEN c.original_score < 75 THEN 1 WHEN c.original_score < 90 THEN 2 ELSE 3 END) = (CASE WHEN c.corrected_score < 60 THEN 0 WHEN c.corrected_score < 75 THEN 1 WHEN c.corrected_score < 90 THEN 2 ELSE 3 END) THEN 1 ELSE 0 END), 0) AS matched FROM aihr_practice_calibration c JOIN latest_calibration latest ON latest.id = c.id WHERE c.tenant_id = '$pilot_tenant'")" read -r calibration_count calibration_matched <<<"$calibration_stats" sop_total="$(pilot_sql_scalar "SELECT COUNT(*) FROM aihr_sop_answer_review WHERE tenant_id = '$pilot_tenant' AND status = '已评审' AND (LOWER(TRIM(COALESCE(category, ''))) = 'sop' OR category LIKE '%SOP%') AND reviewed_time >= '${pilot_start} 00:00:00' AND reviewed_time < DATE_ADD('${pilot_end}', INTERVAL 1 DAY)")" sop_usable="$(pilot_sql_scalar "SELECT COALESCE(SUM(CASE WHEN usable = 1 THEN 1 ELSE 0 END), 0) FROM aihr_sop_answer_review WHERE tenant_id = '$pilot_tenant' AND status = '已评审' AND (LOWER(TRIM(COALESCE(category, ''))) = 'sop' OR category LIKE '%SOP%') AND reviewed_time >= '${pilot_start} 00:00:00' AND reviewed_time < DATE_ADD('${pilot_end}', INTERVAL 1 DAY)")" sop_pending="$(pilot_sql_scalar "SELECT COUNT(*) FROM aihr_sop_answer_review WHERE tenant_id = '$pilot_tenant' AND status <> '已评审' AND (LOWER(TRIM(COALESCE(category, ''))) = 'sop' OR category LIKE '%SOP%') AND create_time >= '${pilot_start} 00:00:00' AND create_time < DATE_ADD('${pilot_end}', INTERVAL 1 DAY)")" satisfaction_count="$(pilot_sql_scalar " $org_identity_cte, formal_sessions AS ( SELECT s.satisfaction_score FROM aihr_practice_session s JOIN org_identity o ON o.identity_key = s.ext_party_id WHERE s.tenant_id = '$pilot_tenant' AND s.mode = 'mobile' AND s.finished_time >= '${pilot_start} 00:00:00' AND s.finished_time < DATE_ADD('${pilot_end}', INTERVAL 1 DAY) ) SELECT COUNT(*) FROM formal_sessions WHERE satisfaction_score IS NOT NULL")" satisfaction_average="$(pilot_sql_scalar " $org_identity_cte, formal_sessions AS ( SELECT s.satisfaction_score FROM aihr_practice_session s JOIN org_identity o ON o.identity_key = s.ext_party_id WHERE s.tenant_id = '$pilot_tenant' AND s.mode = 'mobile' AND s.finished_time >= '${pilot_start} 00:00:00' AND s.finished_time < DATE_ADD('${pilot_end}', INTERVAL 1 DAY) ) SELECT COALESCE(ROUND(AVG(satisfaction_score), 1), 0) FROM formal_sessions WHERE satisfaction_score IS NOT NULL")" echo "OK: pilot gates window=${pilot_start}..${pilot_end} scenarios=${scenario_count}/12 core_flow=service_promotion:${scenario_service_promotion_count} daily_service:${scenario_daily_service_count} sop_docs=${sop_document_count}/5 stored_cases=${case_stored_count}/20 case_unmasked=${case_unmasked_count} ten_sessions=${completed_people}/${pilot_people} org_identity=${org_phone_linked}/${org_active} org_pilot=${org_pilot_people}/20 projects=${org_pilot_projects}/1-2 org_ten_sessions=${org_completed_people}/${org_pilot_people} calibration=${calibration_count}/20 consistency=${calibration_matched}/${calibration_count} sop_usable=${sop_usable}/${sop_total} sop_pending=${sop_pending} satisfaction=${satisfaction_average}/5 responses=${satisfaction_count} audio_schema=${audio_table_count} prompt_version_schema=${prompt_version_schema_count}" [[ "${AIHR_PILOT_STRICT:-false}" != "true" ]] || { (( scenario_count >= 12 )) || fail "pilot enabled scenarios ${scenario_count}/12" (( scenario_service_promotion_count > 0 )) || fail "pilot service-promotion scenarios missing" (( scenario_daily_service_count > 0 )) || fail "pilot daily-service scenarios missing" (( sop_document_count >= 5 )) || fail "pilot residential SOP documents ${sop_document_count}/5" (( case_stored_count >= 20 )) || fail "pilot stored cases ${case_stored_count}/20" (( case_unmasked_count == 0 )) || fail "pilot stored cases contain unmasked known PII (${case_unmasked_count})" (( audio_table_count == 1 )) || fail "pilot audio schema unavailable (aihr_practice_audio missing)" (( prompt_version_schema_count == 1 )) || fail "pilot RAG traceability schema unavailable (aihr_sop_answer_review.prompt_version missing)" (( org_phone_linked >= 20 )) || fail "pilot mobile-login org members ${org_phone_linked}/20 (active org ${org_active})" (( org_pilot_people >= 20 )) || fail "pilot activity matched to active org ${org_pilot_people}/20" (( org_pilot_projects >= 1 && org_pilot_projects <= 2 )) || fail "pilot project scope must be 1-2 projects (actual ${org_pilot_projects})" (( org_completed_people * 100 >= org_pilot_people * 80 )) || fail "pilot org members with at least 10 completed sessions below 80% (${org_completed_people}/${org_pilot_people})" (( calibration_count >= 20 )) || fail "pilot calibration samples ${calibration_count}/20" (( calibration_matched * 100 >= calibration_count * 70 )) || fail "pilot AI/human band consistency below 70%" (( sop_total > 0 )) || fail "pilot SOP review samples missing" (( sop_usable * 100 >= sop_total * 80 )) || fail "pilot SOP usable rate below 80%" (( satisfaction_count > 0 )) || fail "pilot satisfaction responses missing" awk -v average="$satisfaction_average" 'BEGIN { exit !(average >= 4.0) }' || fail "pilot satisfaction average below 4/5" } } if [[ "${AIHR_DEMO_CHECK_LIBRARY_ONLY:-false}" == "true" ]]; then return 0 2>/dev/null || exit 0 fi need curl check_url "frontend" "$FRONTEND_URL/" check_url "mobile-uni" "$MOBILE_URL/h5/" check_mobile_h5_shell contains docs/DEMO_ACCEPTANCE.md "capture-demo-screenshots.mjs" contains scripts/capture-demo-screenshots.mjs "Page.captureScreenshot" contains scripts/capture-demo-screenshots.mjs "wygj-ai-interview-flow.png" tenant_payload="$(curl -k -fsS "$FRONTEND_URL/dev-api/auth/tenant/list")" [[ "$tenant_payload" == *'"tenantId":"000000"'* ]] || fail "tenant list missing tenant 000000" echo "OK: tenant list" code_payload="$(curl -k -fsS "$FRONTEND_URL/dev-api/auth/code")" [[ "$code_payload" == *'"uuid"'* ]] || fail "captcha response missing uuid" echo "OK: captcha" contains frontend/src/router/index.ts "/recruit/interview" contains frontend/src/router/index.ts "/train/practice" contains frontend/src/router/index.ts "/knowledge/cases" contains frontend/src/router/index.ts "/knowledge/sop" contains frontend/src/router/index.ts "/system/model" contains frontend/src/router/index.ts "/admin/org-permission" contains frontend/src/views/admin/org-permission.vue "dryRun ? '同步预检' : '执行同步'" contains frontend/src/views/admin/org-permission.vue "组织同步接口不可用,请检查外部同步配置" contains frontend/src/views/admin/org-permission.vue "row.personName || row.extPartyId" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrOrgSyncService.java "storeDisplayFields" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrOrgSyncService.java "rows.stream().filter(OrgRow::suspectText)" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrOrgSyncService.java "boolean suspectText = hasQuestionMark(projectName)" contains backend/ruoyi-admin/src/main/resources/application-prod.yml "store-display-fields: false" contains mobile-uni/package.json "aihr-mobile-uni" contains mobile-uni/src/pages.json "pages/user/today/index" contains mobile-uni/src/pages.json "pages/user/learning/index" contains mobile-uni/src/pages.json "pages/supervisor/index/index" contains mobile-uni/src/pages.json "pages/candidate/index/index" contains mobile-uni/src/pages.json "pages/candidate/interview/index" contains mobile-uni/src/pages.json "pages/candidate/materials/index" contains mobile-uni/src/pages.json "pages/candidate/progress/index" contains mobile-uni/src/pages.json "pages/candidate/study/index" contains mobile-uni/src/pages/auth/login/index.vue "/resource/sms/code" contains mobile-uni/src/pages/auth/login/index.vue "/auth/mobile/sms-login" contains mobile-uni/src/services/home.ts "/api/aihr/mobile/home/" contains mobile-uni/src/services/home.ts "auth: isLoggedIn()" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrMobileSeedService.java "public HomeResponse publicHome(String role)" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrMobileController.java "return R.ok(mobileSeedService.publicHome(role))" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrMobileController.java "mobileSeedService.home(role, identity)" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrMobileController.java "!UserType.APP_USER.getUserType().equals(loginUser.getUserType())" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrMobileController.java "无权访问该录音" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrMobileController.java "supervisorRoleRequested" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java "public MobileTeamMetrics mobileTeamMetrics(String supervisorExtPartyId)" contains mobile-uni/src/services/practice.ts "/api/train/practice/start" contains mobile-uni/src/services/practice.ts "/api/aihr/mobile/practice/history" contains mobile-uni/src/services/practice.ts "/api/aihr/mobile/profile/promotion-evidence" contains mobile-uni/src/pages/user/practice/index.vue "训练完成,主管端待复盘已更新" contains mobile-uni/src/pages/user/profile/index.vue "贡献度" contains mobile-uni/src/services/knowledge.ts "/api/knowledge/answer-feedback" contains mobile-uni/src/pages/user/sop/index.vue "有帮助" contains mobile-uni/src/pages/user/sop/index.vue "没解决" contains mobile-uni/src/services/knowledge.ts "/api/knowledge/summary-card" contains mobile-uni/src/services/knowledge.ts "/api/knowledge/position-sop" contains mobile-uni/src/services/position.ts "removeStorageSync(roleKey)" contains mobile-uni/src/services/position.ts "/api/aihr/mobile/me" contains mobile-uni/src/services/case.ts "/api/knowledge/case/upload" contains mobile-uni/src/services/case.ts "/api/knowledge/case/capabilities" contains mobile-uni/src/services/case.ts "/api/knowledge/case/records/" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrCaseController.java "/records/{caseId}" contains mobile-uni/src/services/candidate.ts "/api/recruit/interview/start" contains mobile-uni/src/services/candidate.ts "/api/aihr/mobile/candidate/materials" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrCandidateMaterialController.java "候选人登录态无效" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrCandidateMaterialController.java "@SaCheckLogin" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrCandidateMaterialController.java "review(id, request, operator)" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrCandidateMaterialService.java "SUPPORTED_MATERIAL_EXTENSIONS" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrCandidateMaterialService.java "审核接口必须明确提交" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrCaseService.java "SUPPORTED_AUDIO_EXTENSIONS" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrInterviewController.java "@SaCheckLogin" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrSpeechController.java "@SaCheckLogin" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrCaseController.java "@SaCheckLogin" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrSopController.java "restrictDemoVerificationSource(request)" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrSopController.java "StpUtil.hasRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)" contains mobile-uni/src/pages/candidate/interview/index.vue "startCandidateInterview" contains mobile-uni/src/pages/candidate/materials/index.vue "uploadCandidateMaterial" contains mobile-uni/src/pages/candidate/progress/index.vue "buildCandidateFlow" contains mobile-uni/src/pages/candidate/index/index.vue "clearData();" contains mobile-uni/src/pages/candidate/materials/index.vue "clearData();" contains mobile-uni/src/pages/candidate/progress/index.vue "const clearData = () =>" contains mobile-uni/src/pages/candidate/study/index.vue "searchKnowledge" contains mobile-uni/src/pages/user/learning/index.vue "岗位 SOP" contains mobile-uni/src/pages/user/learning/index.vue "当前摘要不代表正式上岗资格" contains mobile-uni/src/pages/user/learning/index.vue "岗位学习范围加载失败" contains mobile-uni/src/pages/user/learning/index.vue "getPracticeAssignments" contains mobile-uni/src/pages/user/learning/index.vue "searchKnowledge" contains mobile-uni/src/pages/user/learning/index.vue "getPracticePrepCard" contains frontend/src/views/recruit/interview.vue "建议复试" contains frontend/src/views/recruit/interview.vue 'v-loading="recordsLoading"' contains frontend/src/views/recruit/interview.vue "const recordsLoading = ref(true)" contains frontend/src/views/train/practice.vue "导师改写" contains frontend/src/views/train/practice.vue 'v-loading="recordsLoading"' contains frontend/src/views/train/practice.vue "const records = ref>([])" contains frontend/src/views/knowledge/cases.vue "真实语音" contains frontend/src/views/knowledge/cases.vue 'v-loading="recordsLoading"' contains frontend/src/views/knowledge/cases.vue "const records = ref([])" contains frontend/src/views/knowledge/cases.vue "案例所属项目" contains frontend/src/views/knowledge/cases.vue "请先选择案例所属项目" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrCaseService.java "系统用户上传案例必须明确所属项目" contains frontend/src/views/index.vue "核心流程覆盖待内容确认" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrDashboardService.java "核心流程覆盖待内容确认" contains frontend/src/views/index.vue "不等于端到端验收" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrDashboardService.java "不等于端到端验收" contains frontend/src/views/index.vue "正式试点身份待核对" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrDashboardService.java "正式试点身份待核对" contains frontend/src/views/index.vue "待同步" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrDashboardService.java "核心流程待确认" contains frontend/src/views/knowledge/sop.vue "训练题已生成" contains frontend/src/views/knowledge/sop.vue "不代表这些文档已真实入库" contains frontend/src/views/knowledge/sop.vue "内置演示" contains frontend/src/views/knowledge/processing.vue "当前显示演示数据,不代表真实库统计" contains frontend/src/views/knowledge/cases.vue "不代表真实案例库数据" contains frontend/src/views/recruit/interview.vue "不代表真实面试数据" contains frontend/src/views/system/model/index.vue "调用优先级" contains frontend/src/views/system/model/index.vue "提示词模板" contains frontend/src/views/system/model/index.vue "不代表真实租户配置" contains frontend/src/views/system/model/index.vue "不代表真实租户模板数据" contains frontend/src/views/system/prompts.vue "保存并启用" contains frontend/src/views/competency/growth.vue "当前运营口径(只读)" contains frontend/src/views/competency/growth.vue "训练画像等级(参考)" contains frontend/src/views/competency/growth.vue "person.personName || person.extPartyId" contains mobile-uni/src/pages/user/profile/index.vue "成长证据包(待人工复核)" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java "训练画像参考等级" contains frontend/src/router/index.ts "/system/prompts" contains frontend/src/api/aihr/interview.ts "/api/recruit/interview/start" contains frontend/src/api/aihr/interview.ts "/api/recruit/interview/candidate-links" contains frontend/src/api/aihr/interview.ts "/records/\${encodeURIComponent(sessionId)}/review" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrInterviewController.java "/api/recruit/interview" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrInterviewController.java "无权查看面试记录" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrInterviewController.java "/records/{sessionId}/review" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrInterviewService.java "reviewed_score" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrInterviewController.java "interviewService.answer(request, currentAppUsername())" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrInterviewController.java "interviewService.finish(request, currentAppUsername())" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrInterviewService.java "面试会话不存在或无权访问" contains frontend/src/views/recruit/interview.vue "人工复核 AI 面试结果" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrInterviewController.java "@SaCheckRole" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrInterviewService.java "aihr_candidate_employee_link" 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.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 "noneMatch(AihrPracticeSeedService::canSeeProject)" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrPracticeController.java "@SaCheckLogin" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrPracticeController.java "sessions/{sessionId}/annotations" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrPromptTemplateController.java "/api/aihr/prompt-templates" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrPromptTemplateController.java "public R> list" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java "mobileCompletedCount" 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 "响应时效" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/domain/AihrPracticeDto.java "MistakeAggregateResponse" contains mobile-uni/src/pages/supervisor/team/index.vue "团队错题本" contains mobile-uni/src/types/api.ts "PracticeMistakeAggregate" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrMobileController.java "/practice/mistakes" contains mobile-uni/src/pages/user/learning/index.vue "错题本" 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/aihr_practice_mysql8.sql "aihr_prompt_template" contains backend/script/sql/aihr_practice_mysql8.sql "summary_card" contains frontend/src/views/train/practice.vue "训练前预习卡" contains frontend/src/views/train/practice.vue "Rubric 预览" contains frontend/src/views/train/practice.vue "提示词模板" contains frontend/src/views/train/practice.vue "导出试点CSV" contains frontend/src/views/train/practice.vue "训练记录接口暂不可用,当前显示演示记录" contains frontend/src/views/train/practice.vue "listPracticeReviews" contains frontend/src/views/train/reviews.vue "导出试点 CSV" contains frontend/src/views/train/reviews.vue "人工校准总分" contains frontend/src/views/train/reviews.vue "不展示演示复盘记录或猜测性试点指标" contains frontend/src/api/aihr/practice.ts "/api/train/practice/export" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java "final_adopted_score" contains backend/script/sql/aihr_practice_mysql8.sql "aihr_practice_calibration" contains frontend/src/api/aihr/practice.ts '/api/train/practice/scenarios/${id}/enabled' contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrPracticeController.java "/scenarios/{id}/enabled" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrModelController.java "@SaCheckRole" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrSopController.java "@SaCheckRole" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/domain/AihrSopDto.java "String position" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrSopController.java "/position-sop" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrSopController.java "@SaIgnore" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrDashboardController.java "@SaCheckLogin" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrDashboardController.java "@SaCheckRole" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java 'firstNonBlank(position, "生活顾问")' contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "normalizeSearchSource" contains frontend/src/views/train/practice.vue "practice_help" contains mobile-uni/src/pages/user/practice/index.vue "loadHelp" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java "turnEvidence" contains mobile-uni/src/pages/user/practice/index.vue "每日三题" contains mobile-uni/src/pages/user/practice/index.vue "每日一练语音已转成文字" contains mobile-uni/src/pages/user/practice/index.vue "dailyAudioOssIds" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java "answer_audio_oss_id" contains backend/script/sql/aihr_practice_mysql8.sql "answer_audio_url" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java "pilot_metric_ready" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrMobileController.java "/practice/satisfaction" contains backend/script/sql/aihr_practice_mysql8.sql "satisfaction_score" contains mobile-uni/src/services/practice.ts "/api/aihr/mobile/practice/satisfaction" contains mobile-uni/src/pages/user/practice/index.vue "本次训练体验如何" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java "incentive_point" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrModelSeedService.java "cost-guard-fallback" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSpeechService.java "Duration.ofSeconds(5)" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrMobileSeedService.java "待复盘对练" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrMobileController.java "/practice/reviews" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrMobileController.java "StpUtil.hasRoleOr(TenantConstants.SUPER_ADMIN_ROLE_KEY, \"hr_operator\")" contains mobile-uni/src/pages/supervisor/review/index.vue "人工贡献分" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrMobileController.java "/practice/reviews/{id}/reviewed" contains mobile-uni/src/pages/supervisor/review/index.vue "标记已复盘" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java "source = 'retry'" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java "isWithinNewHireWindow" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java "allow-legacy-daily-drill-fallback" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java "isLegacyDailyDrillFallbackEnabled" contains backend/ruoyi-admin/src/main/resources/application-dev.yml "allow-legacy-daily-drill-fallback: true" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrOrgSyncService.java "instr(coalesce(person_name, '')" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java "maskSensitiveText" contains mobile-uni/src/services/practice.ts "/api/aihr/mobile/practice/assignments" contains frontend/src/api/aihr/case.ts "/api/knowledge/case/upload" contains frontend/src/api/aihr/case.ts "/api/knowledge/case/records?limit=" contains frontend/src/api/aihr/case.ts "/api/knowledge/case/records/" contains frontend/src/views/knowledge/cases.vue "loadCaseRecords" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrCaseController.java "currentProjectScopes" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrCaseService.java "projectScopesForAppUser" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrCaseController.java "/api/knowledge/case" contains frontend/src/api/aihr/sop.ts "/api/knowledge/search" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrSopController.java "@SaCheckLogin" contains frontend/src/api/aihr/sop.ts "/api/knowledge/doc/upload" contains frontend/src/api/aihr/sop.ts "/api/knowledge/reviews" contains frontend/src/api/aihr/sop.ts "/api/knowledge/answer-feedback" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrSopController.java "reviewAnswerFeedback(id, operator)" contains frontend/src/views/knowledge/sop.vue "SOP 问答评审已记录" contains frontend/src/views/knowledge/sop.vue "答案待复核" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrSopController.java "/api/knowledge" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrSopController.java "/doc/upload" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "仅支持 txt/md/markdown/pdf/doc/docx/xls/xlsx/ppt/pptx/图片文件" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "/embeddings" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "aihr_knowledge_gap" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "hasExplicitSopReviewDecision" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "validateFeedbackFragmentOwnership" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "normalizeFeedbackReviewId" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "AND query_text = ?" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrVideoService.java "处理输出已隐藏" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrUploadQueueService.java "资料加工失败,请重试或联系管理员" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "目录导入失败,请重试或联系管理员" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "文件处理失败,请重试或联系管理员" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrCaseService.java "case audio upload failed(处理错误已隐藏)" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrSpeechController.java "tts audio upload failed(处理错误已隐藏)" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSensitiveText.java "外部 AI 模型前" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrModelSeedService.java "AihrSensitiveText.forModel(prompt)" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrModelSeedService.java "外部响应体已隐藏" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "AihrSensitiveText.forModel(renderPrompt" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSpeechService.java "AihrSensitiveText.forModel(text.trim())" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSpeechService.java "asr http {}(外部响应体已隐藏)" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrVideoService.java "AihrSensitiveText.forModel(fileName)" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrOrgSyncService.java "外部响应体已隐藏" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrCaseController.java "仅项目负责人或主管可提交案例素材" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrCaseController.java "@GetMapping(\"/capabilities\")" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrCaseController.java "StpUtil.hasRoleOr" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrCaseController.java "private boolean canViewCases()" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrCaseService.java "position_level IN ('主管', '项目经理')" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrCaseService.java "media_oss_id" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrCaseService.java "storeSourceAudio" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrCaseService.java "currentCaseOwner" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrInterviewService.java "候选人不存在,请先创建或完成一次面试记录" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrSopController.java "reviewSearch(id, request, operator)" contains mobile-uni/src/pages/user/cases/index.vue "case-audio" contains frontend/src/views/knowledge/cases.vue "case-audio" contains backend/script/sql/aihr_practice_mysql8.sql "aihr_case_record" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "prompt_version" contains backend/script/sql/aihr_practice_mysql8.sql "prompt_version" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "review_id" contains mobile-uni/src/services/knowledge.ts "reviewId: params.reviewId" contains backend/script/sql/aihr_knowledge_mysql8.sql "embedding_json" contains frontend/src/views/knowledge/sop.vue "上传文档" contains frontend/src/views/knowledge/sop.vue ".pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx" contains docs/RUOYI_AI_INCREMENTAL_MIGRATION.md "ruoyi-ai" contains docs/RUOYI_AI_INCREMENTAL_MIGRATION.md "POST /api/knowledge/doc/upload" contains backend/script/sql/aihr_knowledge_mysql8.sql "aihr_knowledge_fragment" contains backend/script/sql/aihr_model_mysql8.sql "aihr_model_config" contains backend/script/sql/aihr_practice_mysql8.sql "aihr_practice_session" contains backend/script/sql/aihr_practice_mysql8.sql "dialogue_json" contains backend/script/sql/aihr_practice_mysql8.sql "incentive_point" contains backend/script/sql/aihr_model_mysql8.sql "api_key" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrModelController.java "/api/aihr/model" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrModelController.java "/providers/{providerCode}" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrModelController.java "/configs/{id}/enabled" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrModelSeedService.java "aihr_model_provider" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrModelSeedService.java "模型调用失败,已回退内置兜底" contains frontend/src/api/aihr/model.ts "/api/aihr/model/providers" contains frontend/src/api/aihr/model.ts "/api/aihr/model/configs" contains frontend/src/views/system/model/index.vue "submitProvider" contains frontend/src/views/system/model/index.vue "submitModel" contains backend/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java "/mobile/sms-login" contains backend/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java "aihr.sms.login-template-id" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrMobileController.java "/api/aihr/mobile" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrMobileController.java "@SaCheckLogin" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrMobileController.java "mobile identity lookup failed, fallback to employee role" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrMobileSeedService.java "candidateHome" echo "OK: demo routes and real-flow markers" echo "OK: AI interview API markers" echo "OK: AI practice API markers" echo "OK: AI case API markers" echo "OK: AI SOP API markers" echo "OK: ruoyi-ai incremental migration markers" echo "OK: AI model migration markers" echo "OK: AI model config page markers" echo "OK: AI mobile role home markers" check_pilot_samples echo "PASS: demo preflight"