fix(aihr): gate pilot schema completeness

This commit is contained in:
2026-07-14 23:19:28 +08:00
parent 3c5ca58425
commit 258cc95696
3 changed files with 18 additions and 5 deletions
+1
View File
@@ -11,6 +11,7 @@
- 2026-07-14 BRD 4.6 案例媒体 schema 迁移:案例服务此前依靠 reset DDL 和运行时懒迁移提供 `media_oss_id/media_url`,旧库和生产库缺少显式版本化迁移;现新增可重复执行的 `aihr_20260714_case_media_mysql8.sql`,并在 schema 回归测试中覆盖“无表先跳过、旧表补列、重复执行不重复加列”。这只解决结构发布一致性,不增加案例业务数据,也不替代正式预渲染视频样片、学习点和主管点评输入。
- 2026-07-14 BRD 4.6 案例媒体严格门禁:正式预检现在同时检查 `aihr_case_record.media_oss_id` 与 `media_url` 两列,避免案例音频回放依赖首次请求触发运行时懒迁移;该检查只判断 schema,不把已有音频或案例数量误判为正式案例学习资产。
- 2026-07-14 23:14 生产 schema 只读复核:生产 `aihr_case_record` 已存在,但 `media_oss_id/media_url` 为 `0/2`;本地提交 `94f48f4c` 的版本化迁移尚未在线上执行。当前只记录差异,不在没有发布窗口确认时直接修改生产数据库。
- 2026-07-14 23:16 生产训练 schema 只读复核:生产缺少 `aihr_practice_session` 的 `dim_task_completion/dim_response_timeliness/response_latency_ms`、`aihr_practice_assignment` 的 `score/score_mode`、SOP 评审的 `prompt_version/requester_ext_party_id` 和组织快照 `hire_date`;对应版本化迁移已存在,但尚未在线上执行。严格门禁现增加会话与每日题字段计数,避免发布后依赖运行时懒迁移。
更新时间:2026-07-14
+6 -2
View File
@@ -81,7 +81,7 @@ check_pilot_samples() {
[[ "$(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 rubric_expected_count rubric_five_dimension_count completed_people pilot_people org_active org_phone_linked org_hire_date_linked org_pilot_people org_pilot_projects org_completed_people audio_table_count audio_upload_table_count case_media_schema_count prompt_version_schema_count requester_identity_schema_count hire_date_schema_count sop_document_count case_stored_count case_unmasked_count
local scenario_count scenario_service_promotion_count scenario_daily_service_count rubric_expected_count rubric_five_dimension_count completed_people pilot_people org_active org_phone_linked org_hire_date_linked org_pilot_people org_pilot_projects org_completed_people audio_table_count audio_upload_table_count case_media_schema_count practice_session_schema_count daily_drill_schema_count prompt_version_schema_count requester_identity_schema_count hire_date_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 (
@@ -142,6 +142,8 @@ check_pilot_samples() {
audio_table_count="$(pilot_sql_scalar "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = 'aihr_practice_audio'")"
audio_upload_table_count="$(pilot_sql_scalar "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = 'aihr_practice_audio_upload'")"
case_media_schema_count="$(pilot_sql_scalar "SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'aihr_case_record' AND column_name IN ('media_oss_id','media_url')")"
practice_session_schema_count="$(pilot_sql_scalar "SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'aihr_practice_session' AND column_name IN ('dim_task_completion','dim_response_timeliness','response_latency_ms','annotations_json','review_advice','incentive_point','satisfaction_score','satisfaction_comment')")"
daily_drill_schema_count="$(pilot_sql_scalar "SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'aihr_practice_assignment' AND column_name IN ('answer_text','feedback','correct','finished_time','score','score_mode')")"
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'")"
requester_identity_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 = 'requester_ext_party_id'")"
hire_date_schema_count="$(pilot_sql_scalar "SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'aihr_org_snapshot' AND column_name = 'hire_date'")"
@@ -298,7 +300,7 @@ check_pilot_samples() {
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 rubric_five_dimensions=${rubric_five_dimension_count}/${rubric_expected_count} 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_hire_date=${org_hire_date_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} audio_upload_schema=${audio_upload_table_count} case_media_schema=${case_media_schema_count}/2 prompt_version_schema=${prompt_version_schema_count} requester_identity_schema=${requester_identity_schema_count} hire_date_schema=${hire_date_schema_count}"
echo "OK: pilot gates window=${pilot_start}..${pilot_end} scenarios=${scenario_count}/12 rubric_five_dimensions=${rubric_five_dimension_count}/${rubric_expected_count} 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_hire_date=${org_hire_date_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} audio_upload_schema=${audio_upload_table_count} case_media_schema=${case_media_schema_count}/2 practice_session_schema=${practice_session_schema_count}/8 daily_drill_schema=${daily_drill_schema_count}/6 prompt_version_schema=${prompt_version_schema_count} requester_identity_schema=${requester_identity_schema_count} hire_date_schema=${hire_date_schema_count}"
[[ "${AIHR_PILOT_STRICT:-false}" != "true" ]] || {
(( scenario_count >= 12 )) || fail "pilot enabled scenarios ${scenario_count}/12"
@@ -311,6 +313,8 @@ check_pilot_samples() {
(( audio_table_count == 1 )) || fail "pilot audio schema unavailable (aihr_practice_audio missing)"
(( audio_upload_table_count == 1 )) || fail "pilot audio staging schema unavailable (aihr_practice_audio_upload missing)"
(( case_media_schema_count == 2 )) || fail "pilot case media schema unavailable (aihr_case_record.media_oss_id/media_url missing)"
(( practice_session_schema_count == 8 )) || fail "pilot practice session schema incomplete (${practice_session_schema_count}/8)"
(( daily_drill_schema_count == 6 )) || fail "pilot daily drill schema incomplete (${daily_drill_schema_count}/6)"
(( prompt_version_schema_count == 1 )) || fail "pilot RAG traceability schema unavailable (aihr_sop_answer_review.prompt_version missing)"
(( requester_identity_schema_count == 1 )) || fail "pilot SOP requester identity schema unavailable (aihr_sop_answer_review.requester_ext_party_id missing)"
(( hire_date_schema_count == 1 )) || fail "pilot org hire_date schema unavailable"
+11 -3
View File
@@ -71,8 +71,15 @@ CREATE TABLE aihr_case_record (
tenant_id varchar(20), status varchar(30), title varchar(120), transcript text, summary varchar(1000),
media_oss_id bigint, media_url varchar(500)
);
CREATE TABLE aihr_practice_session (tenant_id varchar(20), session_id varchar(100), ext_party_id varchar(100), mode varchar(30), finished_time datetime, satisfaction_score tinyint);
CREATE TABLE aihr_practice_assignment (tenant_id varchar(20), ext_party_id varchar(100), create_time datetime);
CREATE TABLE aihr_practice_session (
tenant_id varchar(20), session_id varchar(100), ext_party_id varchar(100), mode varchar(30), finished_time datetime,
dim_task_completion int, dim_response_timeliness int, response_latency_ms int, annotations_json text,
review_advice varchar(1000), incentive_point int, satisfaction_score tinyint, satisfaction_comment varchar(500)
);
CREATE TABLE aihr_practice_assignment (
tenant_id varchar(20), ext_party_id varchar(100), create_time datetime,
answer_text text, feedback varchar(500), correct tinyint, finished_time datetime, score int, score_mode varchar(30)
);
CREATE TABLE aihr_org_snapshot (
tenant_id varchar(20), employment_status varchar(20), ext_party_id varchar(100),
project_code varchar(50), person_phone varchar(30), hire_date date
@@ -121,7 +128,7 @@ INSERT INTO aihr_org_snapshot VALUES
('000000','active','D','P2','13900000004','2026-01-01'),
('000000','active','E','P3','13900000005','2026-01-01'),
('000000','active','F','P3','13900000006','2026-01-01');
INSERT INTO aihr_practice_assignment VALUES
INSERT INTO aihr_practice_assignment (tenant_id, ext_party_id, create_time) VALUES
('000000','D','2026-07-08 08:00:00'),
('000000','13900000001','2026-07-08 08:00:00'),
('000000','E','2026-07-08 08:00:00'),
@@ -170,6 +177,7 @@ pilot_output="$(AIHR_PILOT_START_DATE=2026-07-07 AIHR_PILOT_END_DATE=2026-07-10
[[ "$pilot_output" == *'sop_docs=5/5 stored_cases=20/20'* ]] || { echo "FAIL: BRD content quantity gates: $pilot_output" >&2; exit 1; }
[[ "$pilot_output" == *'case_unmasked=0'* ]] || { echo "FAIL: redacted case content gate: $pilot_output" >&2; exit 1; }
[[ "$pilot_output" == *'case_media_schema=2/2'* ]] || { echo "FAIL: case media schema gate: $pilot_output" >&2; exit 1; }
[[ "$pilot_output" == *'practice_session_schema=8/8 daily_drill_schema=6/6'* ]] || { echo "FAIL: practice schema gates: $pilot_output" >&2; exit 1; }
[[ "$pilot_output" == *'sop_usable=1/1 sop_pending=1'* ]] || { echo "FAIL: SOP window: $pilot_output" >&2; exit 1; }
[[ "$pilot_output" == *'satisfaction=5.0/5 responses=19'* ]] || { echo "FAIL: safe satisfaction identity scope: $pilot_output" >&2; exit 1; }