feat(aihr): close functional checklist gaps

This commit is contained in:
key
2026-08-02 12:15:45 +08:00
parent 699cc08050
commit 6ad8a017a4
80 changed files with 4930 additions and 430 deletions
+71 -11
View File
@@ -5,17 +5,28 @@ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
RESET_SCRIPT="$ROOT_DIR/scripts/reset-dev-db.sh"
LEARNING_CLOSURE_MIGRATION="backend/script/sql/update/aihr_20260717_learning_closure_mysql8.sql"
PRACTICE_GROWTH_MIGRATION="backend/script/sql/update/aihr_20260724_practice_growth_curriculum_mysql8.sql"
LEARNING_TASK_EXAM_BANK_MIGRATION="backend/script/sql/update/aihr_20260802_learning_task_exam_bank_mysql8.sql"
PRACTICE_BASE_SCHEMA="backend/script/sql/aihr_practice_mysql8.sql"
CASE_GOVERNANCE_MIGRATION="backend/script/sql/update/aihr_20260802_case_experience_governance_mysql8.sql"
WEB_AI_BASE_MIGRATION="backend/script/sql/update/aihr_20260717_web_ai_question_reward_mysql8.sql"
WEB_AI_SECRET_MIGRATION="backend/script/sql/update/aihr_20260719_web_ai_database_secret_mysql8.sql"
WEB_SEARCH_QWEN_MIGRATION="backend/script/sql/update/aihr_20260719_web_search_qwen_mysql8.sql"
learning_closure_line="$(grep -nF "$LEARNING_CLOSURE_MIGRATION" "$RESET_SCRIPT" | cut -d: -f1 || true)"
practice_growth_line="$(grep -nF "$PRACTICE_GROWTH_MIGRATION" "$RESET_SCRIPT" | cut -d: -f1 || true)"
if [[ -z "$learning_closure_line" || -z "$practice_growth_line" || "$learning_closure_line" -ge "$practice_growth_line" ]]; then
learning_task_exam_bank_line="$(grep -nF "$LEARNING_TASK_EXAM_BANK_MIGRATION" "$RESET_SCRIPT" | cut -d: -f1 || true)"
if [[ -z "$learning_closure_line" || -z "$practice_growth_line" || -z "$learning_task_exam_bank_line" || "$learning_closure_line" -ge "$practice_growth_line" || "$practice_growth_line" -ge "$learning_task_exam_bank_line" ]]; then
echo "FAIL: reset-dev-db.sh must apply learning closure before practice growth curriculum" >&2
exit 1
fi
practice_base_line="$(grep -nF "$PRACTICE_BASE_SCHEMA" "$RESET_SCRIPT" | cut -d: -f1 || true)"
case_governance_line="$(grep -nF "$CASE_GOVERNANCE_MIGRATION" "$RESET_SCRIPT" | cut -d: -f1 || true)"
if [[ -z "$practice_base_line" || -z "$case_governance_line" || "$practice_base_line" -ge "$case_governance_line" ]]; then
echo "FAIL: reset-dev-db.sh must apply case experience governance after the practice base schema" >&2
exit 1
fi
web_ai_base_line="$(grep -nF "$WEB_AI_BASE_MIGRATION" "$RESET_SCRIPT" | cut -d: -f1 || true)"
web_ai_secret_line="$(grep -nF "$WEB_AI_SECRET_MIGRATION" "$RESET_SCRIPT" | cut -d: -f1 || true)"
web_search_qwen_line="$(grep -nF "$WEB_SEARCH_QWEN_MIGRATION" "$RESET_SCRIPT" | cut -d: -f1 || true)"
@@ -25,15 +36,6 @@ if [[ -z "$web_ai_base_line" || -z "$web_ai_secret_line" || -z "$web_search_qwen
exit 1
fi
if ! command -v docker >/dev/null || ! docker ps --format '{{.Names}}' | grep -qx 'wygj-mysql'; then
echo "SKIP: AIHR schema migration test requires wygj-mysql"
exit 0
fi
MYSQL_CONTAINER="wygj-mysql"
DB="wygj_aihr_schema_test_$$"
LEGACY_DB="wygj_aihr_candidate_legacy_test_$$"
while IFS= read -r migration; do
migration_name="$(basename "$migration")"
grep -Fq "backend/script/sql/update/$migration_name" "$ROOT_DIR/docs/BRD_PRODUCTION_MIGRATION_RUNBOOK.md" || {
@@ -42,6 +44,16 @@ while IFS= read -r migration; do
}
done < <(find "$ROOT_DIR/backend/script/sql/update" -maxdepth 1 -type f -name 'aihr_*_mysql8.sql' -print | sort)
if ! command -v docker >/dev/null || ! docker ps --format '{{.Names}}' | grep -qx 'wygj-mysql'; then
echo "PASS: AIHR migration inventory is documented"
echo "SKIP: AIHR schema migration test requires wygj-mysql"
exit 0
fi
MYSQL_CONTAINER="wygj-mysql"
DB="wygj_aihr_schema_test_$$"
LEGACY_DB="wygj_aihr_candidate_legacy_test_$$"
mysql() {
docker exec -i "$MYSQL_CONTAINER" mysql -uroot -proot --default-character-set=utf8mb4 "$@"
}
@@ -106,6 +118,15 @@ mysql "$DB" < "$ROOT_DIR/backend/script/sql/update/aihr_20260714_case_learning_m
case_learning_columns="$(mysql "$DB" -N -B -e "SELECT GROUP_CONCAT(column_name ORDER BY ordinal_position) FROM information_schema.columns WHERE table_schema='$DB' AND table_name='aihr_case_record' AND column_name IN ('learning_points','supervisor_comment');")"
test "$case_learning_columns" = 'learning_points,supervisor_comment'
case_governance_migration="$ROOT_DIR/backend/script/sql/update/aihr_20260802_case_experience_governance_mysql8.sql"
test -f "$case_governance_migration"
mysql "$DB" < "$case_governance_migration"
mysql "$DB" < "$case_governance_migration"
case_governance_columns="$(mysql "$DB" -N -B -e "SELECT GROUP_CONCAT(column_name ORDER BY ordinal_position) FROM information_schema.columns WHERE table_schema='$DB' AND table_name='aihr_case_record' AND column_name IN ('knowledge_status','source_type','applicable_positions','content_version','source_authorized','deidentified','effective_from','effective_to','release_owner','release_note','released_by','released_time','retire_reason');")"
test "$case_governance_columns" = 'knowledge_status,source_type,applicable_positions,content_version,source_authorized,deidentified,effective_from,effective_to,release_owner,release_note,released_by,released_time,retire_reason'
case_governance_index="$(mysql "$DB" -N -B -e "SELECT GROUP_CONCAT(column_name ORDER BY seq_in_index) FROM information_schema.statistics WHERE table_schema='$DB' AND table_name='aihr_case_record' AND index_name='idx_aihr_case_knowledge_status';")"
test "$case_governance_index" = 'tenant_id,knowledge_status,effective_from,effective_to'
mysql "$DB" <<'SQL'
CREATE TABLE aihr_prompt_template (
id bigint NOT NULL AUTO_INCREMENT,
@@ -178,6 +199,33 @@ mysql "$DB" < "$ROOT_DIR/backend/script/sql/update/aihr_20260715_knowledge_feedb
mysql "$DB" < "$ROOT_DIR/backend/script/sql/update/aihr_20260715_knowledge_feedback_mysql8.sql"
feedback_review_columns="$(mysql "$DB" -N -B -e "SELECT GROUP_CONCAT(column_name ORDER BY ordinal_position) FROM information_schema.columns WHERE table_schema='$DB' AND table_name='aihr_knowledge_answer_feedback' AND column_name IN ('review_id','reviewer','reviewed_time');")"
test "$feedback_review_columns" = 'review_id,reviewer,reviewed_time'
feedback_question_bridge_migration="$ROOT_DIR/backend/script/sql/update/aihr_20260802_feedback_question_bridge_mysql8.sql"
mysql "$DB" < "$feedback_question_bridge_migration"
mysql "$DB" < "$feedback_question_bridge_migration"
feedback_bridge_columns="$(mysql "$DB" -N -B -e "SELECT GROUP_CONCAT(column_name ORDER BY ordinal_position) FROM information_schema.columns WHERE table_schema='$DB' AND table_name='aihr_knowledge_answer_feedback' AND column_name IN ('submitter_user_id','request_id');")"
test "$feedback_bridge_columns" = 'submitter_user_id,request_id'
feedback_bridge_unique="$(mysql "$DB" -N -B -e "SELECT CONCAT(MIN(non_unique),'|',GROUP_CONCAT(column_name ORDER BY seq_in_index),'|',SUM(sub_part IS NOT NULL)) FROM information_schema.statistics WHERE table_schema='$DB' AND table_name='aihr_knowledge_answer_feedback' AND index_name='uk_aihr_answer_feedback_user_request';")"
test "$feedback_bridge_unique" = '0|tenant_id,submitter_user_id,request_id,fragment_id|0'
question_bridge_columns="$(mysql "$DB" -N -B -e "SELECT GROUP_CONCAT(column_name ORDER BY ordinal_position) FROM information_schema.columns WHERE table_schema='$DB' AND table_name='aihr_community_question' AND column_name IN ('source_type','source_request_id','submit_idempotency_key');")"
test "$question_bridge_columns" = 'source_type,source_request_id,submit_idempotency_key'
question_bridge_source_unique="$(mysql "$DB" -N -B -e "SELECT CONCAT(MIN(non_unique),'|',GROUP_CONCAT(column_name ORDER BY seq_in_index),'|',SUM(sub_part IS NOT NULL)) FROM information_schema.statistics WHERE table_schema='$DB' AND table_name='aihr_community_question' AND index_name='uk_aihr_community_question_source';")"
test "$question_bridge_source_unique" = '0|tenant_id,author_user_id,source_type,source_request_id|0'
question_bridge_submit_unique="$(mysql "$DB" -N -B -e "SELECT CONCAT(MIN(non_unique),'|',GROUP_CONCAT(column_name ORDER BY seq_in_index),'|',SUM(sub_part IS NOT NULL)) FROM information_schema.statistics WHERE table_schema='$DB' AND table_name='aihr_community_question' AND index_name='uk_aihr_community_question_submit_key';")"
test "$question_bridge_submit_unique" = '0|tenant_id,author_user_id,submit_idempotency_key|0'
learning_task_exam_bank_migration="$ROOT_DIR/$LEARNING_TASK_EXAM_BANK_MIGRATION"
mysql "$DB" < "$learning_task_exam_bank_migration"
mysql "$DB" < "$learning_task_exam_bank_migration"
learning_question_exam_columns="$(mysql "$DB" -N -B -e "SELECT GROUP_CONCAT(column_name ORDER BY ordinal_position) FROM information_schema.columns WHERE table_schema='$DB' AND table_name='aihr_learning_question' AND column_name IN ('exam_format','exam_options_json','exam_answers_json','exam_explanation','exam_enabled','review_status','review_note','reviewed_by','reviewed_time');")"
test "$learning_question_exam_columns" = 'exam_format,exam_options_json,exam_answers_json,exam_explanation,exam_enabled,review_status,review_note,reviewed_by,reviewed_time'
learning_question_exam_index="$(mysql "$DB" -N -B -e "SELECT CONCAT(MIN(non_unique),'|',GROUP_CONCAT(column_name ORDER BY seq_in_index),'|',SUM(sub_part IS NOT NULL)) FROM information_schema.statistics WHERE table_schema='$DB' AND table_name='aihr_learning_question' AND index_name='idx_aihr_learning_question_exam_pick';")"
test "$learning_question_exam_index" = '1|tenant_id,position_code,review_status,exam_enabled,enabled,id|0'
learning_draw_table="$(mysql "$DB" -N -B -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='$DB' AND table_name='aihr_exam_question_draw';")"
test "$learning_draw_table" = '1'
learning_task_columns="$(mysql "$DB" -N -B -e "SELECT COUNT(*) FROM information_schema.columns WHERE table_schema='$DB' AND table_name='aihr_onboard_task' AND column_name IN ('material_type','material_attachment_id','material_title','material_version','progress_percent','completion_rule','exam_id','recurrence_type','recurrence_series_key','dispatch_request_key');")"
test "$learning_task_columns" = '10'
learning_task_dispatch_index="$(mysql "$DB" -N -B -e "SELECT CONCAT(MIN(non_unique),'|',GROUP_CONCAT(column_name ORDER BY seq_in_index),'|',SUM(sub_part IS NOT NULL)) FROM information_schema.statistics WHERE table_schema='$DB' AND table_name='aihr_onboard_task' AND index_name='uk_aihr_onboard_task_dispatch';")"
test "$learning_task_dispatch_index" = '0|tenant_id,ext_party_id,dispatch_request_key|0'
mysql "$DB" <<'SQL'
CREATE TABLE aihr_practice_session (
@@ -528,12 +576,16 @@ test "$direct_feedback_roles" = '5'
broadcast_attachment_migration="$ROOT_DIR/backend/script/sql/update/aihr_20260724_broadcast_attachment_mysql8.sql"
multi_role_insight_migration="$ROOT_DIR/backend/script/sql/update/aihr_20260725_broadcast_multi_role_insight_mysql8.sql"
broadcast_delivery_controls_migration="$ROOT_DIR/backend/script/sql/update/aihr_20260802_broadcast_delivery_controls_mysql8.sql"
test -f "$broadcast_attachment_migration"
test -f "$multi_role_insight_migration"
test -f "$broadcast_delivery_controls_migration"
mysql "$DB" < "$broadcast_attachment_migration"
mysql "$DB" < "$broadcast_attachment_migration"
mysql "$DB" < "$multi_role_insight_migration"
mysql "$DB" < "$multi_role_insight_migration"
mysql "$DB" < "$broadcast_delivery_controls_migration"
mysql "$DB" < "$broadcast_delivery_controls_migration"
broadcast_attachment_columns="$(mysql "$DB" -N -B -e "SELECT COUNT(*) FROM information_schema.columns WHERE table_schema='$DB' AND table_name='aihr_broadcast_attachment' AND column_name IN ('tenant_id','message_id','oss_id','file_name','file_size','content_type','status','extracted_text','summary','error_message','uploaded_by');")"
test "$broadcast_attachment_columns" = '11'
broadcast_insight_columns="$(mysql "$DB" -N -B -e "SELECT COUNT(*) FROM information_schema.columns WHERE table_schema='$DB' AND table_name='aihr_broadcast_attachment' AND column_name IN ('insight_status','insights_json','insight_version');")"
@@ -546,6 +598,14 @@ broadcast_attachment_message_index="$(mysql "$DB" -N -B -e "SELECT GROUP_CONCAT(
test "$broadcast_attachment_message_index" = 'tenant_id,attachment_id'
broadcast_attachment_bind_index="$(mysql "$DB" -N -B -e "SELECT CONCAT(MIN(non_unique),'|',GROUP_CONCAT(column_name ORDER BY seq_in_index),'|',SUM(sub_part IS NOT NULL)) FROM information_schema.statistics WHERE table_schema='$DB' AND table_name='aihr_broadcast_attachment' AND index_name='uk_aihr_broadcast_attachment_message';")"
test "$broadcast_attachment_bind_index" = '0|tenant_id,message_id|0'
broadcast_delivery_columns="$(mysql "$DB" -N -B -e "SELECT GROUP_CONCAT(CONCAT(column_name,'|',column_type,'|',is_nullable,'|',COALESCE(column_default,'<NULL>')) ORDER BY ordinal_position SEPARATOR ';') FROM information_schema.columns WHERE table_schema='$DB' AND table_name='aihr_broadcast_message' AND column_name IN ('visibility_mode','allow_download');")"
test "$broadcast_delivery_columns" = 'visibility_mode|varchar(20)|NO|ALL;allow_download|tinyint(1)|NO|1'
broadcast_delivery_tables="$(mysql "$DB" -N -B -e "SELECT GROUP_CONCAT(table_name ORDER BY table_name) FROM information_schema.tables WHERE table_schema='$DB' AND table_name IN ('aihr_broadcast_attachment_access_audit','aihr_broadcast_attachment_topic_tag');")"
test "$broadcast_delivery_tables" = 'aihr_broadcast_attachment_access_audit,aihr_broadcast_attachment_topic_tag'
broadcast_topic_unique="$(mysql "$DB" -N -B -e "SELECT CONCAT(MIN(non_unique),'|',GROUP_CONCAT(column_name ORDER BY seq_in_index)) FROM information_schema.statistics WHERE table_schema='$DB' AND table_name='aihr_broadcast_attachment_topic_tag' AND index_name='uk_aihr_broadcast_attachment_topic_tag';")"
test "$broadcast_topic_unique" = '0|tenant_id,attachment_id,tag_name'
broadcast_access_user_index="$(mysql "$DB" -N -B -e "SELECT GROUP_CONCAT(column_name ORDER BY seq_in_index) FROM information_schema.statistics WHERE table_schema='$DB' AND table_name='aihr_broadcast_attachment_access_audit' AND index_name='idx_aihr_broadcast_attachment_access_user';")"
test "$broadcast_access_user_index" = 'tenant_id,user_id,access_time'
mysql "$DB" -e "ALTER TABLE aihr_broadcast_message DROP INDEX uk_aihr_broadcast_message_publish_request, DROP COLUMN publish_request_hash, DROP COLUMN publish_request_key, DROP COLUMN withdraw_reason;"
mysql "$DB" <<'SQL'
@@ -595,4 +655,4 @@ test "$agent_run_unique" = '0|run_id'
agent_user_index="$(mysql "$DB" -N -B -e "SELECT GROUP_CONCAT(column_name ORDER BY seq_in_index) FROM information_schema.statistics WHERE table_schema='$DB' AND table_name='aihr_agent_run' AND index_name='idx_aihr_agent_run_user';")"
test "$agent_user_index" = 'tenant_id,user_id,create_time'
echo "PASS: AIHR legacy schema migrations are idempotent, including Agent run audit, practice growth curriculum snapshots, knowledge-space categories, practice evidence, knowledge feedback, candidate interview review, position/SOP qualification contracts, assistant capture, work-report idempotency, direct feedback, broadcast publish/withdraw audit with M1 targeting and file attachment contracts, v1 snapshots, and trusted broadcast question context"
echo "PASS: AIHR legacy schema migrations are idempotent, including Agent run audit, practice growth curriculum snapshots, knowledge-space categories, practice evidence, knowledge feedback, candidate interview review, position/SOP qualification contracts, assistant capture, work-report idempotency, direct feedback, broadcast publish/withdraw audit with targeting, delivery controls, topic evidence and file access audit, v1 snapshots, and trusted broadcast question context"