sql更新、架构更新

This commit is contained in:
2026-04-16 15:46:29 +08:00
parent 2f2d796e30
commit d5c06eca28
36 changed files with 2099 additions and 387415 deletions

View File

@@ -563,6 +563,13 @@ CREATE TABLE IF NOT EXISTS question.gd_review_plan (
plan_status VARCHAR(32) NOT NULL DEFAULT 'TODO',
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_gd_review_plan_stage
CHECK (plan_stage IN ('E1', 'E2', 'E3', 'E4', 'E5', 'E6', 'E7', 'E8', 'E9', 'E10', 'CUSTOM')),
CONSTRAINT chk_gd_review_plan_status
CHECK (plan_status IN ('TODO', 'DONE', 'SKIPPED', 'EXPIRED')),
CONSTRAINT uq_gd_review_plan_wrong_stage
UNIQUE (wrong_question_id, plan_stage),
CONSTRAINT fk_gd_review_plan_wrong_question
FOREIGN KEY (wrong_question_id) REFERENCES question.gd_wrong_question(wrong_question_id)
);
@@ -570,10 +577,99 @@ COMMENT ON TABLE question.gd_review_plan IS '复习计划表';
COMMENT ON COLUMN question.gd_review_plan.review_plan_id IS '复习计划ID';
COMMENT ON COLUMN question.gd_review_plan.wrong_question_id IS '错题记录ID';
COMMENT ON COLUMN question.gd_review_plan.plan_date IS '计划日期';
COMMENT ON COLUMN question.gd_review_plan.plan_stage IS '阶段E1/E2/E3';
COMMENT ON COLUMN question.gd_review_plan.plan_stage IS '阶段E1-E10/CUSTOM';
COMMENT ON COLUMN question.gd_review_plan.plan_status IS '计划状态';
COMMENT ON COLUMN question.gd_review_plan.tenant_id IS '租户ID';
COMMENT ON COLUMN question.gd_review_plan.created_at IS '创建时间';
COMMENT ON COLUMN question.gd_review_plan.updated_at IS '更新时间';
DROP TABLE IF EXISTS question.gd_review_execution CASCADE;
DROP TABLE IF EXISTS question.gd_review_stage_policy CASCADE;
CREATE TABLE IF NOT EXISTS question.gd_review_stage_policy (
policy_id VARCHAR(64) PRIMARY KEY,
stage_code VARCHAR(16) NOT NULL,
stage_order INTEGER NOT NULL,
interval_days INTEGER NOT NULL,
retry_interval_days INTEGER NOT NULL DEFAULT 1,
pass_threshold NUMERIC(5,2) NOT NULL DEFAULT 60.00,
max_retry_count INTEGER NOT NULL DEFAULT 3,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_gd_review_stage_policy_stage
CHECK (stage_code IN ('E1', 'E2', 'E3', 'E4', 'E5', 'E6', 'E7', 'E8', 'E9', 'E10')),
CONSTRAINT chk_gd_review_stage_policy_stage_order
CHECK (stage_order > 0),
CONSTRAINT chk_gd_review_stage_policy_interval
CHECK (interval_days >= 0),
CONSTRAINT chk_gd_review_stage_policy_retry_interval
CHECK (retry_interval_days >= 0),
CONSTRAINT chk_gd_review_stage_policy_pass_threshold
CHECK (pass_threshold >= 0 AND pass_threshold <= 100),
CONSTRAINT chk_gd_review_stage_policy_max_retry
CHECK (max_retry_count >= 0),
CONSTRAINT uq_gd_review_stage_policy_tenant_stage
UNIQUE (tenant_id, stage_code),
CONSTRAINT uq_gd_review_stage_policy_tenant_order
UNIQUE (tenant_id, stage_order)
);
COMMENT ON TABLE question.gd_review_stage_policy IS '艾宾浩斯复习阶段策略表';
COMMENT ON COLUMN question.gd_review_stage_policy.policy_id IS '策略ID';
COMMENT ON COLUMN question.gd_review_stage_policy.stage_code IS '阶段编码E1-E10';
COMMENT ON COLUMN question.gd_review_stage_policy.stage_order IS '阶段顺序';
COMMENT ON COLUMN question.gd_review_stage_policy.interval_days IS '通过后下阶段间隔天数';
COMMENT ON COLUMN question.gd_review_stage_policy.retry_interval_days IS '失败后重试间隔天数';
COMMENT ON COLUMN question.gd_review_stage_policy.pass_threshold IS '通过阈值0-100';
COMMENT ON COLUMN question.gd_review_stage_policy.max_retry_count IS '当前阶段最大重试次数';
COMMENT ON COLUMN question.gd_review_stage_policy.enabled IS '是否启用';
COMMENT ON COLUMN question.gd_review_stage_policy.tenant_id IS '租户ID';
COMMENT ON COLUMN question.gd_review_stage_policy.created_at IS '创建时间';
COMMENT ON COLUMN question.gd_review_stage_policy.updated_at IS '更新时间';
CREATE TABLE IF NOT EXISTS question.gd_review_execution (
execution_id VARCHAR(64) PRIMARY KEY,
review_plan_id VARCHAR(64) NOT NULL,
wrong_question_id VARCHAR(64) NOT NULL,
student_id VARCHAR(64) NOT NULL,
stage_code VARCHAR(16) NOT NULL,
result_status VARCHAR(16) NOT NULL,
score NUMERIC(8,2),
reviewed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
next_plan_date DATE,
retry_count INTEGER NOT NULL DEFAULT 0,
evidence_json JSONB NOT NULL DEFAULT '{}'::JSONB,
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_gd_review_execution_stage
CHECK (stage_code IN ('E1', 'E2', 'E3', 'E4', 'E5', 'E6', 'E7', 'E8', 'E9', 'E10', 'CUSTOM')),
CONSTRAINT chk_gd_review_execution_result
CHECK (result_status IN ('PASS', 'FAIL', 'SKIPPED')),
CONSTRAINT chk_gd_review_execution_retry
CHECK (retry_count >= 0),
CONSTRAINT chk_gd_review_execution_evidence_json
CHECK (jsonb_typeof(evidence_json) = 'object'),
CONSTRAINT fk_gd_review_execution_plan
FOREIGN KEY (review_plan_id) REFERENCES question.gd_review_plan(review_plan_id) ON DELETE CASCADE,
CONSTRAINT fk_gd_review_execution_wrong_question
FOREIGN KEY (wrong_question_id) REFERENCES question.gd_wrong_question(wrong_question_id),
CONSTRAINT fk_gd_review_execution_student
FOREIGN KEY (student_id) REFERENCES upms.tb_sys_user(user_id)
);
COMMENT ON TABLE question.gd_review_execution IS '复习执行记录表';
COMMENT ON COLUMN question.gd_review_execution.execution_id IS '执行记录ID';
COMMENT ON COLUMN question.gd_review_execution.review_plan_id IS '复习计划ID';
COMMENT ON COLUMN question.gd_review_execution.wrong_question_id IS '错题ID';
COMMENT ON COLUMN question.gd_review_execution.student_id IS '学员ID';
COMMENT ON COLUMN question.gd_review_execution.stage_code IS '复习阶段编码';
COMMENT ON COLUMN question.gd_review_execution.result_status IS '执行结果PASS/FAIL/SKIPPED';
COMMENT ON COLUMN question.gd_review_execution.score IS '本次得分';
COMMENT ON COLUMN question.gd_review_execution.reviewed_at IS '执行时间';
COMMENT ON COLUMN question.gd_review_execution.next_plan_date IS '建议下次复习日期';
COMMENT ON COLUMN question.gd_review_execution.retry_count IS '当前阶段重试次数';
COMMENT ON COLUMN question.gd_review_execution.evidence_json IS '执行证据JSON答案摘要/耗时/反馈等)';
COMMENT ON COLUMN question.gd_review_execution.tenant_id IS '租户ID';
COMMENT ON COLUMN question.gd_review_execution.created_at IS '创建时间';
DROP TABLE IF EXISTS question.gd_teacher_comment CASCADE;
CREATE TABLE IF NOT EXISTS question.gd_teacher_comment (
@@ -604,6 +700,7 @@ DROP TABLE IF EXISTS question.gd_explanation_submission CASCADE;
CREATE TABLE IF NOT EXISTS question.gd_explanation_submission (
explanation_id VARCHAR(64) PRIMARY KEY,
student_id VARCHAR(64) NOT NULL,
question_id VARCHAR(64) NOT NULL,
wrong_question_id VARCHAR(64),
source_submission_id VARCHAR(64),
source_answer_id VARCHAR(64),
@@ -618,6 +715,8 @@ CREATE TABLE IF NOT EXISTS question.gd_explanation_submission (
CHECK (submission_status IN ('SUBMITTED', 'EVALUATED', 'REJECTED')),
CONSTRAINT fk_gd_explanation_submission_student
FOREIGN KEY (student_id) REFERENCES upms.tb_sys_user(user_id),
CONSTRAINT fk_gd_explanation_submission_question
FOREIGN KEY (question_id) REFERENCES question.hw_question_item(question_id),
CONSTRAINT fk_gd_explanation_submission_wrong_question
FOREIGN KEY (wrong_question_id) REFERENCES question.gd_wrong_question(wrong_question_id),
CONSTRAINT fk_gd_explanation_submission_source_submission
@@ -630,6 +729,7 @@ CREATE TABLE IF NOT EXISTS question.gd_explanation_submission (
COMMENT ON TABLE question.gd_explanation_submission IS '费曼讲解提交表';
COMMENT ON COLUMN question.gd_explanation_submission.explanation_id IS '讲解提交ID';
COMMENT ON COLUMN question.gd_explanation_submission.student_id IS '学员ID';
COMMENT ON COLUMN question.gd_explanation_submission.question_id IS '绑定题目ID必填';
COMMENT ON COLUMN question.gd_explanation_submission.wrong_question_id IS '关联错题ID';
COMMENT ON COLUMN question.gd_explanation_submission.source_submission_id IS '来源作业提交ID';
COMMENT ON COLUMN question.gd_explanation_submission.source_answer_id IS '来源答案ID';
@@ -717,13 +817,456 @@ CREATE INDEX IF NOT EXISTS idx_gd_wrong_question_student_status
ON question.gd_wrong_question(student_id, mastery_status);
CREATE INDEX IF NOT EXISTS idx_gd_review_plan_wrong_question_date
ON question.gd_review_plan(wrong_question_id, plan_date);
CREATE INDEX IF NOT EXISTS idx_gd_review_plan_status_date
ON question.gd_review_plan(plan_status, plan_date);
CREATE INDEX IF NOT EXISTS idx_gd_review_plan_stage_status
ON question.gd_review_plan(plan_stage, plan_status, plan_date);
CREATE INDEX IF NOT EXISTS idx_gd_review_stage_policy_tenant_enabled
ON question.gd_review_stage_policy(tenant_id, enabled, stage_order);
CREATE INDEX IF NOT EXISTS idx_gd_review_execution_plan_time
ON question.gd_review_execution(review_plan_id, reviewed_at DESC);
CREATE INDEX IF NOT EXISTS idx_gd_review_execution_wrong_question
ON question.gd_review_execution(wrong_question_id, reviewed_at DESC);
CREATE INDEX IF NOT EXISTS idx_gd_review_execution_student_time
ON question.gd_review_execution(student_id, reviewed_at DESC);
CREATE INDEX IF NOT EXISTS idx_gd_teacher_comment_answer_grade
ON question.gd_teacher_comment(answer_grade_id);
CREATE INDEX IF NOT EXISTS idx_gd_explanation_submission_student
ON question.gd_explanation_submission(student_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_gd_explanation_submission_question
ON question.gd_explanation_submission(question_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_gd_explanation_submission_wrong_question
ON question.gd_explanation_submission(wrong_question_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_gd_explanation_assessment_pass_status
ON question.gd_explanation_assessment(pass_status, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_gd_explanation_dimension_score_assessment
ON question.gd_explanation_dimension_score(assessment_id, dimension_code);
-- recommendation模块并入question习题模块rc_*
DROP TABLE IF EXISTS question.rc_recommendation_task CASCADE;
CREATE TABLE IF NOT EXISTS question.rc_recommendation_task (
task_id VARCHAR(64) PRIMARY KEY,
user_id VARCHAR(64) NOT NULL,
scene_code VARCHAR(32) NOT NULL,
trigger_source VARCHAR(32) NOT NULL DEFAULT 'SCHEDULE',
strategy_version VARCHAR(32),
profile_snapshot_json JSONB NOT NULL DEFAULT '{}'::JSONB,
status VARCHAR(16) NOT NULL DEFAULT 'CREATED',
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_rc_recommendation_task_status
CHECK (status IN ('CREATED', 'RUNNING', 'DONE', 'FAILED')),
CONSTRAINT chk_rc_recommendation_task_profile_json
CHECK (jsonb_typeof(profile_snapshot_json) = 'object'),
CONSTRAINT fk_rc_recommendation_task_user
FOREIGN KEY (user_id) REFERENCES upms.tb_sys_user(user_id)
);
COMMENT ON TABLE question.rc_recommendation_task IS '推荐任务表';
COMMENT ON COLUMN question.rc_recommendation_task.task_id IS '推荐任务ID';
COMMENT ON COLUMN question.rc_recommendation_task.user_id IS '推荐目标用户ID';
COMMENT ON COLUMN question.rc_recommendation_task.scene_code IS '推荐场景编码HOME/WRONGBOOK/REVIEW等';
COMMENT ON COLUMN question.rc_recommendation_task.trigger_source IS '触发来源SCHEDULE/EVENT/MANUAL';
COMMENT ON COLUMN question.rc_recommendation_task.strategy_version IS '推荐策略版本';
COMMENT ON COLUMN question.rc_recommendation_task.profile_snapshot_json IS '画像快照JSON';
COMMENT ON COLUMN question.rc_recommendation_task.status IS '任务状态';
COMMENT ON COLUMN question.rc_recommendation_task.tenant_id IS '租户ID';
COMMENT ON COLUMN question.rc_recommendation_task.created_at IS '创建时间';
COMMENT ON COLUMN question.rc_recommendation_task.updated_at IS '更新时间';
DROP TABLE IF EXISTS question.rc_recommendation_item CASCADE;
CREATE TABLE IF NOT EXISTS question.rc_recommendation_item (
item_id VARCHAR(64) PRIMARY KEY,
task_id VARCHAR(64) NOT NULL,
content_type VARCHAR(32) NOT NULL,
content_object_id VARCHAR(64) NOT NULL,
content_object_type VARCHAR(64) NOT NULL,
rank_no INTEGER NOT NULL,
score NUMERIC(10,6),
reason_codes VARCHAR(255),
metadata_json JSONB NOT NULL DEFAULT '{}'::JSONB,
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_rc_recommendation_item_content_type
CHECK (content_type IN ('COURSE', 'QUESTION', 'PAPER', 'WRONG_QUESTION', 'REVIEW_PLAN', 'MIXED')),
CONSTRAINT chk_rc_recommendation_item_metadata_json
CHECK (jsonb_typeof(metadata_json) = 'object'),
CONSTRAINT uq_rc_recommendation_item_task_rank
UNIQUE (task_id, rank_no),
CONSTRAINT fk_rc_recommendation_item_task
FOREIGN KEY (task_id) REFERENCES question.rc_recommendation_task(task_id) ON DELETE CASCADE
);
COMMENT ON TABLE question.rc_recommendation_item IS '推荐结果明细表';
COMMENT ON COLUMN question.rc_recommendation_item.item_id IS '推荐项ID';
COMMENT ON COLUMN question.rc_recommendation_item.task_id IS '推荐任务ID';
COMMENT ON COLUMN question.rc_recommendation_item.content_type IS '内容类型';
COMMENT ON COLUMN question.rc_recommendation_item.content_object_id IS '内容对象ID';
COMMENT ON COLUMN question.rc_recommendation_item.content_object_type IS '内容对象类型';
COMMENT ON COLUMN question.rc_recommendation_item.rank_no IS '推荐位次';
COMMENT ON COLUMN question.rc_recommendation_item.score IS '推荐分';
COMMENT ON COLUMN question.rc_recommendation_item.reason_codes IS '推荐原因编码集合';
COMMENT ON COLUMN question.rc_recommendation_item.metadata_json IS '扩展元数据JSON';
COMMENT ON COLUMN question.rc_recommendation_item.tenant_id IS '租户ID';
COMMENT ON COLUMN question.rc_recommendation_item.created_at IS '创建时间';
DROP TABLE IF EXISTS question.rc_recommendation_feedback CASCADE;
CREATE TABLE IF NOT EXISTS question.rc_recommendation_feedback (
feedback_id VARCHAR(64) PRIMARY KEY,
item_id VARCHAR(64) NOT NULL,
user_id VARCHAR(64) NOT NULL,
event_type VARCHAR(16) NOT NULL,
event_value NUMERIC(10,4),
event_detail_json JSONB NOT NULL DEFAULT '{}'::JSONB,
event_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_rc_recommendation_feedback_event_type
CHECK (event_type IN ('EXPOSE', 'CLICK', 'START', 'COMPLETE', 'DISLIKE', 'SKIP')),
CONSTRAINT chk_rc_recommendation_feedback_detail_json
CHECK (jsonb_typeof(event_detail_json) = 'object'),
CONSTRAINT fk_rc_recommendation_feedback_item
FOREIGN KEY (item_id) REFERENCES question.rc_recommendation_item(item_id) ON DELETE CASCADE,
CONSTRAINT fk_rc_recommendation_feedback_user
FOREIGN KEY (user_id) REFERENCES upms.tb_sys_user(user_id)
);
COMMENT ON TABLE question.rc_recommendation_feedback IS '推荐反馈事件表';
COMMENT ON COLUMN question.rc_recommendation_feedback.feedback_id IS '反馈事件ID';
COMMENT ON COLUMN question.rc_recommendation_feedback.item_id IS '推荐项ID';
COMMENT ON COLUMN question.rc_recommendation_feedback.user_id IS '用户ID';
COMMENT ON COLUMN question.rc_recommendation_feedback.event_type IS '事件类型';
COMMENT ON COLUMN question.rc_recommendation_feedback.event_value IS '事件值(可选)';
COMMENT ON COLUMN question.rc_recommendation_feedback.event_detail_json IS '事件详情JSON';
COMMENT ON COLUMN question.rc_recommendation_feedback.event_time IS '事件发生时间';
COMMENT ON COLUMN question.rc_recommendation_feedback.tenant_id IS '租户ID';
COMMENT ON COLUMN question.rc_recommendation_feedback.created_at IS '创建时间';
DROP TABLE IF EXISTS question.rc_recommendation_effect_daily CASCADE;
CREATE TABLE IF NOT EXISTS question.rc_recommendation_effect_daily (
stat_date DATE NOT NULL,
scene_code VARCHAR(32) NOT NULL,
strategy_version VARCHAR(32) NOT NULL,
tenant_id VARCHAR(64) NOT NULL,
expose_count BIGINT NOT NULL DEFAULT 0,
click_count BIGINT NOT NULL DEFAULT 0,
complete_count BIGINT NOT NULL DEFAULT 0,
dislike_count BIGINT NOT NULL DEFAULT 0,
ctr NUMERIC(10,6) NOT NULL DEFAULT 0,
complete_rate NUMERIC(10,6) NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (stat_date, scene_code, strategy_version, tenant_id)
);
COMMENT ON TABLE question.rc_recommendation_effect_daily IS '推荐效果日统计表';
COMMENT ON COLUMN question.rc_recommendation_effect_daily.stat_date IS '统计日期';
COMMENT ON COLUMN question.rc_recommendation_effect_daily.scene_code IS '推荐场景编码';
COMMENT ON COLUMN question.rc_recommendation_effect_daily.strategy_version IS '策略版本';
COMMENT ON COLUMN question.rc_recommendation_effect_daily.tenant_id IS '租户ID';
COMMENT ON COLUMN question.rc_recommendation_effect_daily.expose_count IS '曝光次数';
COMMENT ON COLUMN question.rc_recommendation_effect_daily.click_count IS '点击次数';
COMMENT ON COLUMN question.rc_recommendation_effect_daily.complete_count IS '完成次数';
COMMENT ON COLUMN question.rc_recommendation_effect_daily.dislike_count IS '不喜欢次数';
COMMENT ON COLUMN question.rc_recommendation_effect_daily.ctr IS '点击率';
COMMENT ON COLUMN question.rc_recommendation_effect_daily.complete_rate IS '完成率';
COMMENT ON COLUMN question.rc_recommendation_effect_daily.created_at IS '创建时间';
COMMENT ON COLUMN question.rc_recommendation_effect_daily.updated_at IS '更新时间';
CREATE INDEX IF NOT EXISTS idx_rc_recommendation_task_user_scene
ON question.rc_recommendation_task(user_id, scene_code, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_rc_recommendation_task_tenant_status
ON question.rc_recommendation_task(tenant_id, status, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_rc_recommendation_item_task
ON question.rc_recommendation_item(task_id, rank_no);
CREATE INDEX IF NOT EXISTS idx_rc_recommendation_item_content
ON question.rc_recommendation_item(content_object_type, content_object_id);
CREATE INDEX IF NOT EXISTS idx_rc_recommendation_feedback_item_event
ON question.rc_recommendation_feedback(item_id, event_type, event_time DESC);
CREATE INDEX IF NOT EXISTS idx_rc_recommendation_feedback_user_event
ON question.rc_recommendation_feedback(user_id, event_type, event_time DESC);
CREATE INDEX IF NOT EXISTS idx_rc_recommendation_effect_daily_tenant_date
ON question.rc_recommendation_effect_daily(tenant_id, stat_date DESC);
DROP TABLE IF EXISTS question.rc_student_profile_feature CASCADE;
DROP TABLE IF EXISTS question.rc_student_profile_snapshot CASCADE;
CREATE TABLE IF NOT EXISTS question.rc_student_profile_snapshot (
profile_id VARCHAR(64) PRIMARY KEY,
user_id VARCHAR(64) NOT NULL,
profile_version VARCHAR(32) NOT NULL,
profile_date DATE NOT NULL,
feature_source_version VARCHAR(64),
profile_json JSONB NOT NULL DEFAULT '{}'::JSONB,
confidence_score NUMERIC(6,4),
profile_status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uq_rc_student_profile_snapshot_user_version
UNIQUE (user_id, profile_version),
CONSTRAINT chk_rc_student_profile_snapshot_json
CHECK (jsonb_typeof(profile_json) = 'object'),
CONSTRAINT chk_rc_student_profile_snapshot_status
CHECK (profile_status IN ('ACTIVE', 'ARCHIVED')),
CONSTRAINT fk_rc_student_profile_snapshot_user
FOREIGN KEY (user_id) REFERENCES upms.tb_sys_user(user_id)
);
COMMENT ON TABLE question.rc_student_profile_snapshot IS '学员画像快照表';
COMMENT ON COLUMN question.rc_student_profile_snapshot.profile_id IS '画像ID';
COMMENT ON COLUMN question.rc_student_profile_snapshot.user_id IS '学员ID';
COMMENT ON COLUMN question.rc_student_profile_snapshot.profile_version IS '画像版本';
COMMENT ON COLUMN question.rc_student_profile_snapshot.profile_date IS '画像日期';
COMMENT ON COLUMN question.rc_student_profile_snapshot.feature_source_version IS '特征来源版本';
COMMENT ON COLUMN question.rc_student_profile_snapshot.profile_json IS '画像JSON';
COMMENT ON COLUMN question.rc_student_profile_snapshot.confidence_score IS '画像置信度';
COMMENT ON COLUMN question.rc_student_profile_snapshot.profile_status IS '画像状态';
COMMENT ON COLUMN question.rc_student_profile_snapshot.tenant_id IS '租户ID';
COMMENT ON COLUMN question.rc_student_profile_snapshot.created_at IS '创建时间';
COMMENT ON COLUMN question.rc_student_profile_snapshot.updated_at IS '更新时间';
CREATE TABLE IF NOT EXISTS question.rc_student_profile_feature (
feature_id VARCHAR(64) PRIMARY KEY,
profile_id VARCHAR(64) NOT NULL,
feature_group VARCHAR(32) NOT NULL,
feature_key VARCHAR(64) NOT NULL,
feature_value_num NUMERIC(18,6),
feature_value_text VARCHAR(512),
feature_value_json JSONB,
value_type VARCHAR(16) NOT NULL DEFAULT 'NUM',
source_domain VARCHAR(32) NOT NULL,
source_ref_id VARCHAR(64),
sample_time TIMESTAMP,
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_rc_student_profile_feature_value_type
CHECK (value_type IN ('NUM', 'TEXT', 'JSON', 'BOOL')),
CONSTRAINT chk_rc_student_profile_feature_source_domain
CHECK (source_domain IN ('COURSE', 'HOMEWORK', 'GRADING', 'REVIEW', 'RECOMMENDATION', 'ACHIEVEMENT', 'SYSTEM')),
CONSTRAINT chk_rc_student_profile_feature_json
CHECK (feature_value_json IS NULL OR jsonb_typeof(feature_value_json) IN ('object', 'array', 'string', 'number', 'boolean')),
CONSTRAINT fk_rc_student_profile_feature_profile
FOREIGN KEY (profile_id) REFERENCES question.rc_student_profile_snapshot(profile_id) ON DELETE CASCADE
);
COMMENT ON TABLE question.rc_student_profile_feature IS '学员画像特征明细表';
COMMENT ON COLUMN question.rc_student_profile_feature.feature_id IS '特征ID';
COMMENT ON COLUMN question.rc_student_profile_feature.profile_id IS '画像ID';
COMMENT ON COLUMN question.rc_student_profile_feature.feature_group IS '特征组';
COMMENT ON COLUMN question.rc_student_profile_feature.feature_key IS '特征键';
COMMENT ON COLUMN question.rc_student_profile_feature.feature_value_num IS '数值特征值';
COMMENT ON COLUMN question.rc_student_profile_feature.feature_value_text IS '文本特征值';
COMMENT ON COLUMN question.rc_student_profile_feature.feature_value_json IS 'JSON特征值';
COMMENT ON COLUMN question.rc_student_profile_feature.value_type IS '值类型';
COMMENT ON COLUMN question.rc_student_profile_feature.source_domain IS '来源域';
COMMENT ON COLUMN question.rc_student_profile_feature.source_ref_id IS '来源对象ID';
COMMENT ON COLUMN question.rc_student_profile_feature.sample_time IS '采样时间';
COMMENT ON COLUMN question.rc_student_profile_feature.tenant_id IS '租户ID';
COMMENT ON COLUMN question.rc_student_profile_feature.created_at IS '创建时间';
DROP TABLE IF EXISTS question.rc_learning_loop_event CASCADE;
DROP TABLE IF EXISTS question.rc_learning_loop_case CASCADE;
CREATE TABLE IF NOT EXISTS question.rc_learning_loop_case (
loop_case_id VARCHAR(64) PRIMARY KEY,
user_id VARCHAR(64) NOT NULL,
wrong_question_id VARCHAR(64) NOT NULL,
review_plan_id VARCHAR(64),
profile_id VARCHAR(64),
recommendation_task_id VARCHAR(64),
recommendation_item_id VARCHAR(64),
feedback_id VARCHAR(64),
user_achievement_id VARCHAR(64),
loop_status VARCHAR(16) NOT NULL DEFAULT 'OPEN',
effect_score NUMERIC(10,4),
opened_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
closed_at TIMESTAMP,
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_rc_learning_loop_case_status
CHECK (loop_status IN ('OPEN', 'IN_PROGRESS', 'CLOSED', 'FAILED')),
CONSTRAINT chk_rc_learning_loop_case_closed_at
CHECK (closed_at IS NULL OR closed_at >= opened_at),
CONSTRAINT fk_rc_learning_loop_case_user
FOREIGN KEY (user_id) REFERENCES upms.tb_sys_user(user_id),
CONSTRAINT fk_rc_learning_loop_case_wrong_question
FOREIGN KEY (wrong_question_id) REFERENCES question.gd_wrong_question(wrong_question_id),
CONSTRAINT fk_rc_learning_loop_case_review_plan
FOREIGN KEY (review_plan_id) REFERENCES question.gd_review_plan(review_plan_id),
CONSTRAINT fk_rc_learning_loop_case_profile
FOREIGN KEY (profile_id) REFERENCES question.rc_student_profile_snapshot(profile_id),
CONSTRAINT fk_rc_learning_loop_case_recommendation_task
FOREIGN KEY (recommendation_task_id) REFERENCES question.rc_recommendation_task(task_id),
CONSTRAINT fk_rc_learning_loop_case_recommendation_item
FOREIGN KEY (recommendation_item_id) REFERENCES question.rc_recommendation_item(item_id),
CONSTRAINT fk_rc_learning_loop_case_feedback
FOREIGN KEY (feedback_id) REFERENCES question.rc_recommendation_feedback(feedback_id)
);
COMMENT ON TABLE question.rc_learning_loop_case IS '学习闭环主表(错题-复习-推荐-成就)';
COMMENT ON COLUMN question.rc_learning_loop_case.loop_case_id IS '闭环案例ID';
COMMENT ON COLUMN question.rc_learning_loop_case.user_id IS '用户ID';
COMMENT ON COLUMN question.rc_learning_loop_case.wrong_question_id IS '错题ID';
COMMENT ON COLUMN question.rc_learning_loop_case.review_plan_id IS '复习计划ID';
COMMENT ON COLUMN question.rc_learning_loop_case.profile_id IS '画像ID';
COMMENT ON COLUMN question.rc_learning_loop_case.recommendation_task_id IS '推荐任务ID';
COMMENT ON COLUMN question.rc_learning_loop_case.recommendation_item_id IS '推荐项ID';
COMMENT ON COLUMN question.rc_learning_loop_case.feedback_id IS '反馈事件ID';
COMMENT ON COLUMN question.rc_learning_loop_case.user_achievement_id IS '成就记录ID';
COMMENT ON COLUMN question.rc_learning_loop_case.loop_status IS '闭环状态';
COMMENT ON COLUMN question.rc_learning_loop_case.effect_score IS '闭环效果分';
COMMENT ON COLUMN question.rc_learning_loop_case.opened_at IS '闭环开启时间';
COMMENT ON COLUMN question.rc_learning_loop_case.closed_at IS '闭环关闭时间';
COMMENT ON COLUMN question.rc_learning_loop_case.tenant_id IS '租户ID';
COMMENT ON COLUMN question.rc_learning_loop_case.created_at IS '创建时间';
COMMENT ON COLUMN question.rc_learning_loop_case.updated_at IS '更新时间';
CREATE TABLE IF NOT EXISTS question.rc_learning_loop_event (
loop_event_id VARCHAR(64) PRIMARY KEY,
loop_case_id VARCHAR(64) NOT NULL,
stage_code VARCHAR(32) NOT NULL,
stage_status VARCHAR(16) NOT NULL DEFAULT 'DONE',
event_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
event_score NUMERIC(10,4),
event_detail_json JSONB NOT NULL DEFAULT '{}'::JSONB,
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_rc_learning_loop_event_stage_code
CHECK (stage_code IN ('WRONG_CAPTURED', 'REVIEW_PLANNED', 'PROFILE_UPDATED', 'RECOMMENDED', 'EXPOSED', 'ENGAGED', 'REVIEW_COMPLETED', 'ACHIEVED')),
CONSTRAINT chk_rc_learning_loop_event_stage_status
CHECK (stage_status IN ('PENDING', 'DONE', 'FAILED', 'SKIPPED')),
CONSTRAINT chk_rc_learning_loop_event_detail_json
CHECK (jsonb_typeof(event_detail_json) = 'object'),
CONSTRAINT fk_rc_learning_loop_event_case
FOREIGN KEY (loop_case_id) REFERENCES question.rc_learning_loop_case(loop_case_id) ON DELETE CASCADE
);
COMMENT ON TABLE question.rc_learning_loop_event IS '学习闭环阶段事件表';
COMMENT ON COLUMN question.rc_learning_loop_event.loop_event_id IS '闭环事件ID';
COMMENT ON COLUMN question.rc_learning_loop_event.loop_case_id IS '闭环案例ID';
COMMENT ON COLUMN question.rc_learning_loop_event.stage_code IS '阶段编码';
COMMENT ON COLUMN question.rc_learning_loop_event.stage_status IS '阶段状态';
COMMENT ON COLUMN question.rc_learning_loop_event.event_time IS '事件时间';
COMMENT ON COLUMN question.rc_learning_loop_event.event_score IS '阶段得分';
COMMENT ON COLUMN question.rc_learning_loop_event.event_detail_json IS '事件详情JSON';
COMMENT ON COLUMN question.rc_learning_loop_event.tenant_id IS '租户ID';
COMMENT ON COLUMN question.rc_learning_loop_event.created_at IS '创建时间';
DROP TABLE IF EXISTS question.rc_learning_loop_effect_daily CASCADE;
CREATE TABLE IF NOT EXISTS question.rc_learning_loop_effect_daily (
stat_date DATE NOT NULL,
tenant_id VARCHAR(64) NOT NULL,
opened_case_count BIGINT NOT NULL DEFAULT 0,
closed_case_count BIGINT NOT NULL DEFAULT 0,
achieved_case_count BIGINT NOT NULL DEFAULT 0,
avg_effect_score NUMERIC(10,4) NOT NULL DEFAULT 0,
avg_close_hours NUMERIC(10,4) NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (stat_date, tenant_id)
);
COMMENT ON TABLE question.rc_learning_loop_effect_daily IS '学习闭环效果日统计表';
COMMENT ON COLUMN question.rc_learning_loop_effect_daily.stat_date IS '统计日期';
COMMENT ON COLUMN question.rc_learning_loop_effect_daily.tenant_id IS '租户ID';
COMMENT ON COLUMN question.rc_learning_loop_effect_daily.opened_case_count IS '开启案例数';
COMMENT ON COLUMN question.rc_learning_loop_effect_daily.closed_case_count IS '关闭案例数';
COMMENT ON COLUMN question.rc_learning_loop_effect_daily.achieved_case_count IS '达成成就案例数';
COMMENT ON COLUMN question.rc_learning_loop_effect_daily.avg_effect_score IS '平均效果分';
COMMENT ON COLUMN question.rc_learning_loop_effect_daily.avg_close_hours IS '平均闭环时长(小时)';
COMMENT ON COLUMN question.rc_learning_loop_effect_daily.created_at IS '创建时间';
COMMENT ON COLUMN question.rc_learning_loop_effect_daily.updated_at IS '更新时间';
CREATE INDEX IF NOT EXISTS idx_rc_student_profile_snapshot_user_date
ON question.rc_student_profile_snapshot(user_id, profile_date DESC);
CREATE INDEX IF NOT EXISTS idx_rc_student_profile_snapshot_tenant_version
ON question.rc_student_profile_snapshot(tenant_id, profile_version, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_rc_student_profile_feature_profile_group
ON question.rc_student_profile_feature(profile_id, feature_group, feature_key);
CREATE INDEX IF NOT EXISTS idx_rc_student_profile_feature_source
ON question.rc_student_profile_feature(source_domain, source_ref_id);
CREATE INDEX IF NOT EXISTS idx_rc_learning_loop_case_user_status
ON question.rc_learning_loop_case(user_id, loop_status, opened_at DESC);
CREATE INDEX IF NOT EXISTS idx_rc_learning_loop_case_wrong_question
ON question.rc_learning_loop_case(wrong_question_id, opened_at DESC);
CREATE INDEX IF NOT EXISTS idx_rc_learning_loop_case_tenant_status
ON question.rc_learning_loop_case(tenant_id, loop_status, opened_at DESC);
CREATE INDEX IF NOT EXISTS idx_rc_learning_loop_event_case_stage_time
ON question.rc_learning_loop_event(loop_case_id, stage_code, event_time DESC);
CREATE INDEX IF NOT EXISTS idx_rc_learning_loop_event_tenant_time
ON question.rc_learning_loop_event(tenant_id, event_time DESC);
CREATE INDEX IF NOT EXISTS idx_rc_learning_loop_effect_daily_tenant_date
ON question.rc_learning_loop_effect_daily(tenant_id, stat_date DESC);
DROP TABLE IF EXISTS question.rc_loop_metric_map CASCADE;
DROP TABLE IF EXISTS question.rc_loop_stage_achievement_map CASCADE;
CREATE TABLE IF NOT EXISTS question.rc_loop_stage_achievement_map (
stage_map_id VARCHAR(64) PRIMARY KEY,
stage_code VARCHAR(32) NOT NULL,
event_code VARCHAR(64) NOT NULL,
template_id VARCHAR(64),
metric_id VARCHAR(64),
award_source VARCHAR(32) NOT NULL DEFAULT 'RULE_ENGINE',
priority INTEGER NOT NULL DEFAULT 100,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
ext_json JSONB NOT NULL DEFAULT '{}'::JSONB,
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uq_rc_loop_stage_achievement_map
UNIQUE (tenant_id, stage_code, event_code, priority),
CONSTRAINT chk_rc_loop_stage_achievement_map_stage_code
CHECK (stage_code IN ('WRONG_CAPTURED', 'REVIEW_PLANNED', 'PROFILE_UPDATED', 'RECOMMENDED', 'EXPOSED', 'ENGAGED', 'REVIEW_COMPLETED', 'ACHIEVED')),
CONSTRAINT chk_rc_loop_stage_achievement_map_award_source
CHECK (award_source IN ('RULE_ENGINE', 'MANUAL', 'SYSTEM')),
CONSTRAINT chk_rc_loop_stage_achievement_map_ext_json
CHECK (jsonb_typeof(ext_json) = 'object')
);
COMMENT ON TABLE question.rc_loop_stage_achievement_map IS '闭环阶段到成就事件映射表(通用配置)';
COMMENT ON COLUMN question.rc_loop_stage_achievement_map.stage_map_id IS '阶段映射ID';
COMMENT ON COLUMN question.rc_loop_stage_achievement_map.stage_code IS '闭环阶段编码';
COMMENT ON COLUMN question.rc_loop_stage_achievement_map.event_code IS '成就事件编码';
COMMENT ON COLUMN question.rc_loop_stage_achievement_map.template_id IS '成就规则模板ID';
COMMENT ON COLUMN question.rc_loop_stage_achievement_map.metric_id IS '成就指标ID';
COMMENT ON COLUMN question.rc_loop_stage_achievement_map.award_source IS '发放来源';
COMMENT ON COLUMN question.rc_loop_stage_achievement_map.priority IS '优先级';
COMMENT ON COLUMN question.rc_loop_stage_achievement_map.enabled IS '是否启用';
COMMENT ON COLUMN question.rc_loop_stage_achievement_map.ext_json IS '扩展配置JSON';
COMMENT ON COLUMN question.rc_loop_stage_achievement_map.tenant_id IS '租户ID';
COMMENT ON COLUMN question.rc_loop_stage_achievement_map.created_at IS '创建时间';
COMMENT ON COLUMN question.rc_loop_stage_achievement_map.updated_at IS '更新时间';
CREATE TABLE IF NOT EXISTS question.rc_loop_metric_map (
metric_map_id VARCHAR(64) PRIMARY KEY,
stage_code VARCHAR(32) NOT NULL,
metric_id VARCHAR(64) NOT NULL,
source_field VARCHAR(64) NOT NULL,
agg_method VARCHAR(16) NOT NULL DEFAULT 'LATEST',
weight NUMERIC(6,4) NOT NULL DEFAULT 1.0000,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
ext_json JSONB NOT NULL DEFAULT '{}'::JSONB,
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uq_rc_loop_metric_map
UNIQUE (tenant_id, stage_code, metric_id, source_field),
CONSTRAINT chk_rc_loop_metric_map_stage_code
CHECK (stage_code IN ('WRONG_CAPTURED', 'REVIEW_PLANNED', 'PROFILE_UPDATED', 'RECOMMENDED', 'EXPOSED', 'ENGAGED', 'REVIEW_COMPLETED', 'ACHIEVED')),
CONSTRAINT chk_rc_loop_metric_map_agg_method
CHECK (agg_method IN ('LATEST', 'SUM', 'AVG', 'MAX', 'MIN', 'COUNT')),
CONSTRAINT chk_rc_loop_metric_map_weight
CHECK (weight > 0 AND weight <= 1),
CONSTRAINT chk_rc_loop_metric_map_ext_json
CHECK (jsonb_typeof(ext_json) = 'object')
);
COMMENT ON TABLE question.rc_loop_metric_map IS '闭环指标映射表(通用配置)';
COMMENT ON COLUMN question.rc_loop_metric_map.metric_map_id IS '指标映射ID';
COMMENT ON COLUMN question.rc_loop_metric_map.stage_code IS '闭环阶段编码';
COMMENT ON COLUMN question.rc_loop_metric_map.metric_id IS '成就指标ID';
COMMENT ON COLUMN question.rc_loop_metric_map.source_field IS '来源字段名';
COMMENT ON COLUMN question.rc_loop_metric_map.agg_method IS '聚合方法';
COMMENT ON COLUMN question.rc_loop_metric_map.weight IS '权重';
COMMENT ON COLUMN question.rc_loop_metric_map.enabled IS '是否启用';
COMMENT ON COLUMN question.rc_loop_metric_map.ext_json IS '扩展配置JSON';
COMMENT ON COLUMN question.rc_loop_metric_map.tenant_id IS '租户ID';
COMMENT ON COLUMN question.rc_loop_metric_map.created_at IS '创建时间';
COMMENT ON COLUMN question.rc_loop_metric_map.updated_at IS '更新时间';
CREATE INDEX IF NOT EXISTS idx_rc_loop_stage_achievement_map_tenant_stage
ON question.rc_loop_stage_achievement_map(tenant_id, stage_code, enabled, priority);
CREATE INDEX IF NOT EXISTS idx_rc_loop_stage_achievement_map_event
ON question.rc_loop_stage_achievement_map(event_code, tenant_id, enabled);
CREATE INDEX IF NOT EXISTS idx_rc_loop_metric_map_tenant_stage
ON question.rc_loop_metric_map(tenant_id, stage_code, enabled);