Files
K12Study/init/pg/question/10_create_question_tables.sql
2026-04-16 15:46:29 +08:00

1273 lines
77 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

DROP SCHEMA IF EXISTS question CASCADE;
CREATE SCHEMA IF NOT EXISTS question;
DROP TABLE IF EXISTS question.hw_question_bank CASCADE;
CREATE TABLE IF NOT EXISTS question.hw_question_bank (
bank_id VARCHAR(64) PRIMARY KEY,
bank_name VARCHAR(256) NOT NULL,
subject_code VARCHAR(32) NOT NULL,
grade_code VARCHAR(32) NOT NULL,
status VARCHAR(32) NOT NULL DEFAULT 'ACTIVE',
adcode VARCHAR(12) NOT NULL,
tenant_id VARCHAR(64) NOT NULL,
tenant_path VARCHAR(255) NOT NULL,
dept_id VARCHAR(64),
dept_path VARCHAR(255),
created_by VARCHAR(64),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE question.hw_question_bank IS '题库表';
COMMENT ON COLUMN question.hw_question_bank.bank_id IS '题库ID';
COMMENT ON COLUMN question.hw_question_bank.bank_name IS '题库名称';
COMMENT ON COLUMN question.hw_question_bank.subject_code IS '学科编码';
COMMENT ON COLUMN question.hw_question_bank.grade_code IS '年级编码';
COMMENT ON COLUMN question.hw_question_bank.status IS '状态';
COMMENT ON COLUMN question.hw_question_bank.adcode IS '行政区划编码';
COMMENT ON COLUMN question.hw_question_bank.tenant_id IS '租户ID';
COMMENT ON COLUMN question.hw_question_bank.tenant_path IS '租户路径';
COMMENT ON COLUMN question.hw_question_bank.dept_id IS '部门ID';
COMMENT ON COLUMN question.hw_question_bank.dept_path IS '部门路径';
COMMENT ON COLUMN question.hw_question_bank.created_by IS '创建人ID';
COMMENT ON COLUMN question.hw_question_bank.created_at IS '创建时间';
DROP TABLE IF EXISTS question.hw_question_item CASCADE;
CREATE TABLE IF NOT EXISTS question.hw_question_item (
question_id VARCHAR(64) PRIMARY KEY,
question_type VARCHAR(32) NOT NULL,
stem TEXT NOT NULL,
stem_json JSONB,
difficulty VARCHAR(16),
answer_payload JSONB,
analysis TEXT,
scoring_rule_json JSONB,
question_status VARCHAR(32) 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 chk_hw_question_item_type
CHECK (question_type IN ('SINGLE_CHOICE', 'MULTIPLE_CHOICE', 'FILL_BLANK', 'JUDGE', 'SUBJECTIVE')),
CONSTRAINT chk_hw_question_item_answer_payload
CHECK (answer_payload IS NOT NULL AND jsonb_typeof(answer_payload) = 'object')
);
COMMENT ON TABLE question.hw_question_item IS '题目表(含标准答案)';
COMMENT ON COLUMN question.hw_question_item.question_id IS '题目ID';
COMMENT ON COLUMN question.hw_question_item.question_type IS '题型';
COMMENT ON COLUMN question.hw_question_item.stem IS '题干';
COMMENT ON COLUMN question.hw_question_item.stem_json IS '结构化题干JSON富文本/图片公式等)';
COMMENT ON COLUMN question.hw_question_item.difficulty IS '难度';
COMMENT ON COLUMN question.hw_question_item.answer_payload IS '统一标准答案载荷JSONB对应后端题型答案类型';
COMMENT ON COLUMN question.hw_question_item.analysis IS '题目解析';
COMMENT ON COLUMN question.hw_question_item.scoring_rule_json IS '评分规则JSON含主观题评分点';
COMMENT ON COLUMN question.hw_question_item.question_status IS '题目状态ACTIVE/DISABLED';
COMMENT ON COLUMN question.hw_question_item.tenant_id IS '租户ID';
COMMENT ON COLUMN question.hw_question_item.created_at IS '创建时间';
COMMENT ON COLUMN question.hw_question_item.updated_at IS '更新时间';
DROP TABLE IF EXISTS question.hw_bank_question_rel CASCADE;
CREATE TABLE IF NOT EXISTS question.hw_bank_question_rel (
bank_id VARCHAR(64) NOT NULL,
question_id VARCHAR(64) NOT NULL,
question_order INTEGER,
source_type VARCHAR(32) NOT NULL DEFAULT 'MANUAL',
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (bank_id, question_id),
CONSTRAINT chk_hw_bank_question_rel_source_type
CHECK (source_type IN ('MANUAL', 'AI_GENERATED', 'IMPORTED', 'MIGRATED')),
CONSTRAINT fk_hw_bank_question_rel_bank
FOREIGN KEY (bank_id) REFERENCES question.hw_question_bank(bank_id),
CONSTRAINT fk_hw_bank_question_rel_question
FOREIGN KEY (question_id) REFERENCES question.hw_question_item(question_id)
);
COMMENT ON TABLE question.hw_bank_question_rel IS '题库-题目关联表';
COMMENT ON COLUMN question.hw_bank_question_rel.bank_id IS '题库ID';
COMMENT ON COLUMN question.hw_bank_question_rel.question_id IS '题目ID';
COMMENT ON COLUMN question.hw_bank_question_rel.question_order IS '题库内题目顺序';
COMMENT ON COLUMN question.hw_bank_question_rel.source_type IS '题目来源类型MANUAL/AI_GENERATED/IMPORTED/MIGRATED';
COMMENT ON COLUMN question.hw_bank_question_rel.tenant_id IS '租户ID';
COMMENT ON COLUMN question.hw_bank_question_rel.created_at IS '创建时间';
DROP TABLE IF EXISTS question.hw_question_kp_rel CASCADE;
CREATE TABLE IF NOT EXISTS question.hw_question_kp_rel (
question_id VARCHAR(64) NOT NULL,
kp_id VARCHAR(64) NOT NULL,
relation_type VARCHAR(32) NOT NULL DEFAULT 'TESTS',
weight NUMERIC(6,4) NOT NULL DEFAULT 1.0000,
confidence NUMERIC(5,4),
graph_relation_id VARCHAR(64),
source_table VARCHAR(128),
source_pk VARCHAR(128),
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (question_id, kp_id),
CONSTRAINT chk_hw_question_kp_rel_type
CHECK (relation_type IN ('TESTS', 'RELATED', 'SUPPORTS')),
CONSTRAINT chk_hw_question_kp_rel_weight
CHECK (weight > 0 AND weight <= 1),
CONSTRAINT chk_hw_question_kp_rel_confidence
CHECK (confidence IS NULL OR (confidence >= 0 AND confidence <= 1)),
CONSTRAINT fk_hw_question_kp_rel_question
FOREIGN KEY (question_id) REFERENCES question.hw_question_item(question_id),
CONSTRAINT fk_hw_question_kp_rel_kp
FOREIGN KEY (kp_id) REFERENCES course.cl_knowledge_point(kp_id)
);
COMMENT ON TABLE question.hw_question_kp_rel IS '题目-知识点关联表';
COMMENT ON COLUMN question.hw_question_kp_rel.question_id IS '题目ID';
COMMENT ON COLUMN question.hw_question_kp_rel.kp_id IS '知识点ID';
COMMENT ON COLUMN question.hw_question_kp_rel.relation_type IS '关系类型TESTS/RELATED/SUPPORTS';
COMMENT ON COLUMN question.hw_question_kp_rel.weight IS '关联权重0,1]';
COMMENT ON COLUMN question.hw_question_kp_rel.confidence IS '抽取置信度';
COMMENT ON COLUMN question.hw_question_kp_rel.graph_relation_id IS '图谱关系ID';
COMMENT ON COLUMN question.hw_question_kp_rel.source_table IS '来源表';
COMMENT ON COLUMN question.hw_question_kp_rel.source_pk IS '来源主键';
COMMENT ON COLUMN question.hw_question_kp_rel.tenant_id IS '租户ID';
COMMENT ON COLUMN question.hw_question_kp_rel.created_at IS '创建时间';
COMMENT ON COLUMN question.hw_question_kp_rel.updated_at IS '更新时间';
DROP TABLE IF EXISTS question.hw_paper CASCADE;
CREATE TABLE IF NOT EXISTS question.hw_paper (
paper_id VARCHAR(64) PRIMARY KEY,
paper_name VARCHAR(256) NOT NULL,
paper_type VARCHAR(32) NOT NULL DEFAULT 'STANDARD',
subject_code VARCHAR(32) NOT NULL,
total_score NUMERIC(8,2) NOT NULL DEFAULT 100,
source_file_id VARCHAR(64),
adcode VARCHAR(12) NOT NULL,
tenant_id VARCHAR(64) NOT NULL,
tenant_path VARCHAR(255) NOT NULL,
created_by VARCHAR(64),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
grading_policy_json JSONB NOT NULL DEFAULT '{}'::JSONB,
CONSTRAINT chk_hw_paper_type
CHECK (paper_type IN ('STANDARD', 'EXTERNAL_UPLOAD')),
CONSTRAINT chk_hw_paper_grading_policy_json
CHECK (jsonb_typeof(grading_policy_json) = 'object'),
CONSTRAINT fk_hw_paper_source_file
FOREIGN KEY (source_file_id) REFERENCES upms.tb_sys_file(file_id)
);
COMMENT ON TABLE question.hw_paper IS '试卷表';
COMMENT ON COLUMN question.hw_paper.paper_id IS '试卷ID';
COMMENT ON COLUMN question.hw_paper.paper_name IS '试卷名称';
COMMENT ON COLUMN question.hw_paper.paper_type IS '试卷类型STANDARD/EXTERNAL_UPLOAD';
COMMENT ON COLUMN question.hw_paper.subject_code IS '学科编码';
COMMENT ON COLUMN question.hw_paper.total_score IS '总分';
COMMENT ON COLUMN question.hw_paper.source_file_id IS '来源文件ID课外上传作业解析来源';
COMMENT ON COLUMN question.hw_paper.adcode IS '行政区划编码';
COMMENT ON COLUMN question.hw_paper.tenant_id IS '租户ID';
COMMENT ON COLUMN question.hw_paper.tenant_path IS '租户路径';
COMMENT ON COLUMN question.hw_paper.created_by IS '创建人';
COMMENT ON COLUMN question.hw_paper.created_at IS '创建时间';
COMMENT ON COLUMN question.hw_paper.grading_policy_json IS '试卷批改策略JSON单选/判断完全匹配、多选得分策略、填空匹配策略、简答评估模式)';
DROP TABLE IF EXISTS question.hw_paper_question CASCADE;
CREATE TABLE IF NOT EXISTS question.hw_paper_question (
paper_id VARCHAR(64) NOT NULL,
question_id VARCHAR(64) NOT NULL,
question_order INTEGER NOT NULL,
score NUMERIC(8,2) NOT NULL DEFAULT 0,
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (paper_id, question_id),
CONSTRAINT fk_hw_paper_question_paper
FOREIGN KEY (paper_id) REFERENCES question.hw_paper(paper_id),
CONSTRAINT fk_hw_paper_question_question
FOREIGN KEY (question_id) REFERENCES question.hw_question_item(question_id)
);
COMMENT ON TABLE question.hw_paper_question IS '试卷-题目关联表';
COMMENT ON COLUMN question.hw_paper_question.paper_id IS '试卷ID';
COMMENT ON COLUMN question.hw_paper_question.question_id IS '题目ID';
COMMENT ON COLUMN question.hw_paper_question.question_order IS '题目顺序';
COMMENT ON COLUMN question.hw_paper_question.score IS '题目分值';
COMMENT ON COLUMN question.hw_paper_question.tenant_id IS '租户ID';
COMMENT ON COLUMN question.hw_paper_question.created_at IS '创建时间';
DROP TABLE IF EXISTS question.hw_assignment CASCADE;
CREATE TABLE IF NOT EXISTS question.hw_assignment (
assignment_id VARCHAR(64) PRIMARY KEY,
paper_id VARCHAR(64) NOT NULL,
assignment_mode VARCHAR(32) NOT NULL DEFAULT 'PAPER',
title VARCHAR(256) NOT NULL,
publish_time TIMESTAMP,
deadline TIMESTAMP,
status VARCHAR(32) NOT NULL DEFAULT 'DRAFT',
adcode VARCHAR(12) NOT NULL,
tenant_id VARCHAR(64) NOT NULL,
tenant_path VARCHAR(255) NOT NULL,
dept_id VARCHAR(64),
dept_path VARCHAR(255),
created_by VARCHAR(64),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_hw_assignment_mode
CHECK (assignment_mode IN ('PAPER', 'EXTERNAL_UPLOAD')),
CONSTRAINT fk_hw_assignment_paper
FOREIGN KEY (paper_id) REFERENCES question.hw_paper(paper_id)
);
COMMENT ON TABLE question.hw_assignment IS '作业表';
COMMENT ON COLUMN question.hw_assignment.assignment_id IS '作业ID';
COMMENT ON COLUMN question.hw_assignment.paper_id IS '试卷ID';
COMMENT ON COLUMN question.hw_assignment.assignment_mode IS '作业模式PAPER/EXTERNAL_UPLOAD';
COMMENT ON COLUMN question.hw_assignment.title IS '作业标题';
COMMENT ON COLUMN question.hw_assignment.publish_time IS '发布时间';
COMMENT ON COLUMN question.hw_assignment.deadline IS '截止时间';
COMMENT ON COLUMN question.hw_assignment.status IS '状态DRAFT/PUBLISHED/CLOSED';
COMMENT ON COLUMN question.hw_assignment.adcode IS '行政区划编码';
COMMENT ON COLUMN question.hw_assignment.tenant_id IS '租户ID';
COMMENT ON COLUMN question.hw_assignment.tenant_path IS '租户路径';
COMMENT ON COLUMN question.hw_assignment.dept_id IS '部门ID';
COMMENT ON COLUMN question.hw_assignment.dept_path IS '部门路径';
COMMENT ON COLUMN question.hw_assignment.created_by IS '创建人';
COMMENT ON COLUMN question.hw_assignment.created_at IS '创建时间';
DROP TABLE IF EXISTS question.hw_assignment_target CASCADE;
CREATE TABLE IF NOT EXISTS question.hw_assignment_target (
target_id VARCHAR(64) PRIMARY KEY,
assignment_id VARCHAR(64) NOT NULL,
target_type VARCHAR(32) NOT NULL,
target_ref_id VARCHAR(64) NOT NULL,
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_hw_assignment_target_assignment
FOREIGN KEY (assignment_id) REFERENCES question.hw_assignment(assignment_id)
);
COMMENT ON TABLE question.hw_assignment_target IS '作业投放对象表';
COMMENT ON COLUMN question.hw_assignment_target.target_id IS '投放记录ID';
COMMENT ON COLUMN question.hw_assignment_target.assignment_id IS '作业ID';
COMMENT ON COLUMN question.hw_assignment_target.target_type IS '投放类型CLASS/STUDENT';
COMMENT ON COLUMN question.hw_assignment_target.target_ref_id IS '投放对象ID';
COMMENT ON COLUMN question.hw_assignment_target.tenant_id IS '租户ID';
COMMENT ON COLUMN question.hw_assignment_target.created_at IS '创建时间';
DROP TABLE IF EXISTS question.hw_submission CASCADE;
CREATE TABLE IF NOT EXISTS question.hw_submission (
submission_id VARCHAR(64) PRIMARY KEY,
assignment_id VARCHAR(64) NOT NULL,
student_id VARCHAR(64) NOT NULL,
submission_mode VARCHAR(32) NOT NULL DEFAULT 'ASSIGNMENT',
origin_file_id VARCHAR(64),
submit_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
used_seconds INTEGER,
status VARCHAR(32) NOT NULL DEFAULT 'SUBMITTED',
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_hw_submission_mode
CHECK (submission_mode IN ('ASSIGNMENT', 'EXTERNAL_UPLOAD')),
CONSTRAINT fk_hw_submission_assignment
FOREIGN KEY (assignment_id) REFERENCES question.hw_assignment(assignment_id),
CONSTRAINT fk_hw_submission_student
FOREIGN KEY (student_id) REFERENCES upms.tb_sys_user(user_id),
CONSTRAINT fk_hw_submission_origin_file
FOREIGN KEY (origin_file_id) REFERENCES upms.tb_sys_file(file_id)
);
COMMENT ON TABLE question.hw_submission IS '作业提交表';
COMMENT ON COLUMN question.hw_submission.submission_id IS '提交ID';
COMMENT ON COLUMN question.hw_submission.assignment_id IS '作业ID';
COMMENT ON COLUMN question.hw_submission.student_id IS '学员ID';
COMMENT ON COLUMN question.hw_submission.submission_mode IS '提交模式ASSIGNMENT/EXTERNAL_UPLOAD';
COMMENT ON COLUMN question.hw_submission.origin_file_id IS '原始上传文件ID课外作业';
COMMENT ON COLUMN question.hw_submission.submit_time IS '提交时间';
COMMENT ON COLUMN question.hw_submission.used_seconds IS '作答耗时(秒)';
COMMENT ON COLUMN question.hw_submission.status IS '提交状态';
COMMENT ON COLUMN question.hw_submission.tenant_id IS '租户ID';
COMMENT ON COLUMN question.hw_submission.created_at IS '创建时间';
DROP TABLE IF EXISTS question.hw_submission_answer CASCADE;
CREATE TABLE IF NOT EXISTS question.hw_submission_answer (
answer_id VARCHAR(64) PRIMARY KEY,
submission_id VARCHAR(64) NOT NULL,
question_id VARCHAR(64) NOT NULL,
answer_type VARCHAR(32) NOT NULL,
answer_payload JSONB,
file_id VARCHAR(64),
file_type VARCHAR(32),
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_hw_submission_answer_type
CHECK (answer_type IN ('SINGLE_CHOICE', 'MULTIPLE_CHOICE', 'FILL_BLANK', 'JUDGE', 'SUBJECTIVE')),
CONSTRAINT chk_hw_submission_answer_payload
CHECK (
answer_payload IS NOT NULL
OR file_id IS NOT NULL
),
CONSTRAINT chk_hw_submission_answer_payload_json
CHECK (answer_payload IS NULL OR jsonb_typeof(answer_payload) = 'object'),
CONSTRAINT chk_hw_submission_answer_file_type
CHECK (file_type IS NULL OR file_type IN ('IMAGE', 'AUDIO', 'VIDEO', 'DOCUMENT', 'OTHER')),
CONSTRAINT chk_hw_submission_answer_file_ref
CHECK (
(file_id IS NULL AND file_type IS NULL)
OR (file_id IS NOT NULL AND file_type IS NOT NULL)
),
CONSTRAINT uq_hw_submission_answer_submission_question
UNIQUE (submission_id, question_id),
CONSTRAINT fk_hw_submission_answer_submission
FOREIGN KEY (submission_id) REFERENCES question.hw_submission(submission_id),
CONSTRAINT fk_hw_submission_answer_question
FOREIGN KEY (question_id) REFERENCES question.hw_question_item(question_id),
CONSTRAINT fk_hw_submission_answer_file
FOREIGN KEY (file_id) REFERENCES upms.tb_sys_file(file_id)
);
COMMENT ON TABLE question.hw_submission_answer IS '提交答案明细表(一次提交中每题一行)';
COMMENT ON COLUMN question.hw_submission_answer.answer_id IS '答案ID';
COMMENT ON COLUMN question.hw_submission_answer.submission_id IS '提交ID';
COMMENT ON COLUMN question.hw_submission_answer.question_id IS '题目ID';
COMMENT ON COLUMN question.hw_submission_answer.answer_type IS '答案类型(与题型一致)';
COMMENT ON COLUMN question.hw_submission_answer.answer_payload IS '统一作答载荷JSONB对应后端答案类型';
COMMENT ON COLUMN question.hw_submission_answer.file_id IS '作答文件ID图片/音频等)';
COMMENT ON COLUMN question.hw_submission_answer.file_type IS '作答文件类型';
COMMENT ON COLUMN question.hw_submission_answer.tenant_id IS '租户ID';
COMMENT ON COLUMN question.hw_submission_answer.created_at IS '创建时间';
COMMENT ON COLUMN question.hw_submission_answer.updated_at IS '更新时间';
CREATE INDEX IF NOT EXISTS idx_hw_question_bank_tenant_subject_grade
ON question.hw_question_bank(tenant_id, subject_code, grade_code);
CREATE INDEX IF NOT EXISTS idx_hw_question_item_type
ON question.hw_question_item(question_type, difficulty);
CREATE INDEX IF NOT EXISTS idx_hw_question_item_tenant
ON question.hw_question_item(tenant_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_hw_bank_question_rel_question
ON question.hw_bank_question_rel(question_id, bank_id);
CREATE INDEX IF NOT EXISTS idx_hw_bank_question_rel_tenant_bank
ON question.hw_bank_question_rel(tenant_id, bank_id, question_order);
CREATE INDEX IF NOT EXISTS idx_hw_question_kp_rel_tenant_question
ON question.hw_question_kp_rel(tenant_id, question_id);
CREATE INDEX IF NOT EXISTS idx_hw_question_kp_rel_tenant_kp
ON question.hw_question_kp_rel(tenant_id, kp_id);
CREATE INDEX IF NOT EXISTS idx_hw_question_kp_rel_graph_relation
ON question.hw_question_kp_rel(graph_relation_id);
CREATE INDEX IF NOT EXISTS idx_hw_assignment_tenant_status_deadline
ON question.hw_assignment(tenant_id, status, deadline);
CREATE INDEX IF NOT EXISTS idx_hw_assignment_mode
ON question.hw_assignment(assignment_mode, tenant_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_hw_assignment_target_assignment
ON question.hw_assignment_target(assignment_id, target_type);
CREATE INDEX IF NOT EXISTS idx_hw_submission_assignment_student
ON question.hw_submission(assignment_id, student_id);
CREATE INDEX IF NOT EXISTS idx_hw_submission_mode
ON question.hw_submission(submission_mode, tenant_id, submit_time DESC);
CREATE INDEX IF NOT EXISTS idx_hw_submission_origin_file
ON question.hw_submission(origin_file_id);
CREATE INDEX IF NOT EXISTS idx_hw_submission_answer_submission
ON question.hw_submission_answer(submission_id);
CREATE INDEX IF NOT EXISTS idx_hw_submission_answer_question
ON question.hw_submission_answer(question_id);
CREATE INDEX IF NOT EXISTS idx_hw_submission_answer_file_id
ON question.hw_submission_answer(file_id);
DROP TABLE IF EXISTS question.gd_grading_task CASCADE;
CREATE TABLE IF NOT EXISTS question.gd_grading_task (
grading_task_id VARCHAR(64) PRIMARY KEY,
submission_id VARCHAR(64) NOT NULL,
paper_id VARCHAR(64) NOT NULL,
task_status VARCHAR(32) NOT NULL DEFAULT 'PENDING',
trigger_source VARCHAR(32) NOT NULL DEFAULT 'SUBMISSION',
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_gd_grading_task_submission
FOREIGN KEY (submission_id) REFERENCES question.hw_submission(submission_id),
CONSTRAINT fk_gd_grading_task_paper
FOREIGN KEY (paper_id) REFERENCES question.hw_paper(paper_id)
);
COMMENT ON TABLE question.gd_grading_task IS '批改任务表';
COMMENT ON COLUMN question.gd_grading_task.grading_task_id IS '批改任务ID';
COMMENT ON COLUMN question.gd_grading_task.submission_id IS '提交ID';
COMMENT ON COLUMN question.gd_grading_task.paper_id IS '试卷ID';
COMMENT ON COLUMN question.gd_grading_task.task_status IS '任务状态PENDING/RUNNING/WAIT_REVIEW/DONE';
COMMENT ON COLUMN question.gd_grading_task.trigger_source IS '触发来源';
COMMENT ON COLUMN question.gd_grading_task.tenant_id IS '租户ID';
COMMENT ON COLUMN question.gd_grading_task.created_at IS '创建时间';
COMMENT ON COLUMN question.gd_grading_task.updated_at IS '更新时间';
DROP TABLE IF EXISTS question.gd_answer_grade CASCADE;
CREATE TABLE IF NOT EXISTS question.gd_answer_grade (
answer_grade_id VARCHAR(64) PRIMARY KEY,
grading_task_id VARCHAR(64) NOT NULL,
answer_id VARCHAR(64) NOT NULL,
paper_id VARCHAR(64) NOT NULL,
grade_mode VARCHAR(32) NOT NULL DEFAULT 'AUTO',
grade_status VARCHAR(32) NOT NULL DEFAULT 'PENDING',
score NUMERIC(8,2),
grader_id VARCHAR(64),
grade_comment TEXT,
evidence_json JSONB,
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_answer_grade_mode
CHECK (grade_mode IN ('AUTO', 'MANUAL', 'AI', 'AI_REVIEW')),
CONSTRAINT chk_gd_answer_grade_status
CHECK (grade_status IN ('PENDING', 'DONE', 'REVIEWED')),
CONSTRAINT uq_gd_answer_grade_task_answer
UNIQUE (grading_task_id, answer_id),
CONSTRAINT fk_gd_answer_grade_task
FOREIGN KEY (grading_task_id) REFERENCES question.gd_grading_task(grading_task_id),
CONSTRAINT fk_gd_answer_grade_answer
FOREIGN KEY (answer_id) REFERENCES question.hw_submission_answer(answer_id),
CONSTRAINT fk_gd_answer_grade_paper
FOREIGN KEY (paper_id) REFERENCES question.hw_paper(paper_id),
CONSTRAINT fk_gd_answer_grade_grader
FOREIGN KEY (grader_id) REFERENCES upms.tb_sys_user(user_id)
);
COMMENT ON TABLE question.gd_answer_grade IS '答案批改结果表(统一客观/主观)';
COMMENT ON COLUMN question.gd_answer_grade.answer_grade_id IS '答案批改结果ID';
COMMENT ON COLUMN question.gd_answer_grade.grading_task_id IS '批改任务ID';
COMMENT ON COLUMN question.gd_answer_grade.answer_id IS '答案ID';
COMMENT ON COLUMN question.gd_answer_grade.paper_id IS '试卷ID';
COMMENT ON COLUMN question.gd_answer_grade.grade_mode IS '批改模式AUTO/MANUAL/AI/AI_REVIEW';
COMMENT ON COLUMN question.gd_answer_grade.grade_status IS '批改状态PENDING/DONE/REVIEWED';
COMMENT ON COLUMN question.gd_answer_grade.score IS '最终得分';
COMMENT ON COLUMN question.gd_answer_grade.grader_id IS '评阅人ID';
COMMENT ON COLUMN question.gd_answer_grade.grade_comment IS '批改评语';
COMMENT ON COLUMN question.gd_answer_grade.evidence_json IS '判定依据JSON';
COMMENT ON COLUMN question.gd_answer_grade.tenant_id IS '租户ID';
COMMENT ON COLUMN question.gd_answer_grade.created_at IS '创建时间';
COMMENT ON COLUMN question.gd_answer_grade.updated_at IS '更新时间';
DROP TABLE IF EXISTS question.gd_score_summary CASCADE;
CREATE TABLE IF NOT EXISTS question.gd_score_summary (
summary_id VARCHAR(64) PRIMARY KEY,
grading_task_id VARCHAR(64) UNIQUE NOT NULL,
total_score NUMERIC(8,2) NOT NULL DEFAULT 0,
grade_level VARCHAR(8),
surpass_ratio NUMERIC(5,2),
used_seconds INTEGER,
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_gd_score_summary_task
FOREIGN KEY (grading_task_id) REFERENCES question.gd_grading_task(grading_task_id)
);
COMMENT ON TABLE question.gd_score_summary IS '批改结果汇总表';
COMMENT ON COLUMN question.gd_score_summary.summary_id IS '汇总ID';
COMMENT ON COLUMN question.gd_score_summary.grading_task_id IS '批改任务ID';
COMMENT ON COLUMN question.gd_score_summary.total_score IS '总分';
COMMENT ON COLUMN question.gd_score_summary.grade_level IS '等级A/B/C';
COMMENT ON COLUMN question.gd_score_summary.surpass_ratio IS '超越比例';
COMMENT ON COLUMN question.gd_score_summary.used_seconds IS '作答耗时';
COMMENT ON COLUMN question.gd_score_summary.tenant_id IS '租户ID';
COMMENT ON COLUMN question.gd_score_summary.created_at IS '创建时间';
DROP TABLE IF EXISTS question.gd_error_tag CASCADE;
CREATE TABLE IF NOT EXISTS question.gd_error_tag (
error_tag_id VARCHAR(64) PRIMARY KEY,
tag_code VARCHAR(64) UNIQUE NOT NULL,
tag_name VARCHAR(128) NOT NULL,
category VARCHAR(32) NOT NULL,
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE question.gd_error_tag IS '错因标签表';
COMMENT ON COLUMN question.gd_error_tag.error_tag_id IS '标签ID';
COMMENT ON COLUMN question.gd_error_tag.tag_code IS '标签编码';
COMMENT ON COLUMN question.gd_error_tag.tag_name IS '标签名称';
COMMENT ON COLUMN question.gd_error_tag.category IS '分类(审题/计算/概念等)';
COMMENT ON COLUMN question.gd_error_tag.tenant_id IS '租户ID';
COMMENT ON COLUMN question.gd_error_tag.created_at IS '创建时间';
DROP TABLE IF EXISTS question.gd_answer_error_rel CASCADE;
CREATE TABLE IF NOT EXISTS question.gd_answer_error_rel (
answer_id VARCHAR(64) NOT NULL,
error_tag_id VARCHAR(64) NOT NULL,
confidence NUMERIC(5,4),
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (answer_id, error_tag_id),
CONSTRAINT fk_gd_answer_error_rel_answer
FOREIGN KEY (answer_id) REFERENCES question.hw_submission_answer(answer_id),
CONSTRAINT fk_gd_answer_error_rel_tag
FOREIGN KEY (error_tag_id) REFERENCES question.gd_error_tag(error_tag_id)
);
COMMENT ON TABLE question.gd_answer_error_rel IS '答案-错因标签关联表';
COMMENT ON COLUMN question.gd_answer_error_rel.answer_id IS '答案ID';
COMMENT ON COLUMN question.gd_answer_error_rel.error_tag_id IS '错因标签ID';
COMMENT ON COLUMN question.gd_answer_error_rel.confidence IS '置信度';
COMMENT ON COLUMN question.gd_answer_error_rel.tenant_id IS '租户ID';
COMMENT ON COLUMN question.gd_answer_error_rel.created_at IS '创建时间';
DROP TABLE IF EXISTS question.gd_answer_kp_analysis CASCADE;
CREATE TABLE IF NOT EXISTS question.gd_answer_kp_analysis (
answer_id VARCHAR(64) NOT NULL,
kp_id VARCHAR(64) NOT NULL,
grading_task_id VARCHAR(64),
correctness VARCHAR(16) NOT NULL DEFAULT 'UNKNOWN',
mastery_score NUMERIC(6,4),
confidence NUMERIC(5,4),
evidence_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,
PRIMARY KEY (answer_id, kp_id),
CONSTRAINT chk_gd_answer_kp_analysis_correctness
CHECK (correctness IN ('CORRECT', 'PARTIAL', 'WRONG', 'UNKNOWN')),
CONSTRAINT chk_gd_answer_kp_analysis_mastery
CHECK (mastery_score IS NULL OR (mastery_score >= 0 AND mastery_score <= 1)),
CONSTRAINT chk_gd_answer_kp_analysis_confidence
CHECK (confidence IS NULL OR (confidence >= 0 AND confidence <= 1)),
CONSTRAINT chk_gd_answer_kp_analysis_evidence_json
CHECK (jsonb_typeof(evidence_json) = 'object'),
CONSTRAINT fk_gd_answer_kp_analysis_answer
FOREIGN KEY (answer_id) REFERENCES question.hw_submission_answer(answer_id),
CONSTRAINT fk_gd_answer_kp_analysis_kp
FOREIGN KEY (kp_id) REFERENCES course.cl_knowledge_point(kp_id),
CONSTRAINT fk_gd_answer_kp_analysis_grading_task
FOREIGN KEY (grading_task_id) REFERENCES question.gd_grading_task(grading_task_id)
);
COMMENT ON TABLE question.gd_answer_kp_analysis IS '答案-知识点分析表';
COMMENT ON COLUMN question.gd_answer_kp_analysis.answer_id IS '答案ID';
COMMENT ON COLUMN question.gd_answer_kp_analysis.kp_id IS '知识点ID';
COMMENT ON COLUMN question.gd_answer_kp_analysis.grading_task_id IS '批改任务ID';
COMMENT ON COLUMN question.gd_answer_kp_analysis.correctness IS '正确性CORRECT/PARTIAL/WRONG/UNKNOWN';
COMMENT ON COLUMN question.gd_answer_kp_analysis.mastery_score IS '答案层知识点掌握度0-1';
COMMENT ON COLUMN question.gd_answer_kp_analysis.confidence IS '分析置信度';
COMMENT ON COLUMN question.gd_answer_kp_analysis.evidence_json IS '知识点分析依据JSON';
COMMENT ON COLUMN question.gd_answer_kp_analysis.tenant_id IS '租户ID';
COMMENT ON COLUMN question.gd_answer_kp_analysis.created_at IS '创建时间';
COMMENT ON COLUMN question.gd_answer_kp_analysis.updated_at IS '更新时间';
DROP TABLE IF EXISTS question.gd_wrong_question CASCADE;
CREATE TABLE IF NOT EXISTS question.gd_wrong_question (
wrong_question_id VARCHAR(64) PRIMARY KEY,
student_id VARCHAR(64) NOT NULL,
question_id VARCHAR(64) NOT NULL,
source_submission_id VARCHAR(64) NOT NULL,
mastery_status VARCHAR(32) NOT NULL DEFAULT 'PENDING',
review_count INTEGER NOT NULL DEFAULT 0,
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_gd_wrong_question_student
FOREIGN KEY (student_id) REFERENCES upms.tb_sys_user(user_id),
CONSTRAINT fk_gd_wrong_question_question
FOREIGN KEY (question_id) REFERENCES question.hw_question_item(question_id),
CONSTRAINT fk_gd_wrong_question_submission
FOREIGN KEY (source_submission_id) REFERENCES question.hw_submission(submission_id)
);
COMMENT ON TABLE question.gd_wrong_question IS '错题沉淀表';
COMMENT ON COLUMN question.gd_wrong_question.wrong_question_id IS '错题记录ID';
COMMENT ON COLUMN question.gd_wrong_question.student_id IS '学员ID';
COMMENT ON COLUMN question.gd_wrong_question.question_id IS '题目ID';
COMMENT ON COLUMN question.gd_wrong_question.source_submission_id IS '来源提交ID';
COMMENT ON COLUMN question.gd_wrong_question.mastery_status IS '掌握状态';
COMMENT ON COLUMN question.gd_wrong_question.review_count IS '复习次数';
COMMENT ON COLUMN question.gd_wrong_question.tenant_id IS '租户ID';
COMMENT ON COLUMN question.gd_wrong_question.created_at IS '创建时间';
COMMENT ON COLUMN question.gd_wrong_question.updated_at IS '更新时间';
DROP TABLE IF EXISTS question.gd_review_plan CASCADE;
CREATE TABLE IF NOT EXISTS question.gd_review_plan (
review_plan_id VARCHAR(64) PRIMARY KEY,
wrong_question_id VARCHAR(64) NOT NULL,
plan_date DATE NOT NULL,
plan_stage VARCHAR(16) NOT NULL,
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)
);
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-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 (
comment_id VARCHAR(64) PRIMARY KEY,
answer_grade_id VARCHAR(64) NOT NULL,
reviewer_id VARCHAR(64) NOT NULL,
comment_type VARCHAR(16) NOT NULL DEFAULT 'TEXT',
content_ref TEXT,
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_gd_teacher_comment_answer_grade
FOREIGN KEY (answer_grade_id) REFERENCES question.gd_answer_grade(answer_grade_id),
CONSTRAINT fk_gd_teacher_comment_reviewer
FOREIGN KEY (reviewer_id) REFERENCES upms.tb_sys_user(user_id)
);
COMMENT ON TABLE question.gd_teacher_comment IS '教师点评表';
COMMENT ON COLUMN question.gd_teacher_comment.comment_id IS '点评ID';
COMMENT ON COLUMN question.gd_teacher_comment.answer_grade_id IS '答案批改结果ID';
COMMENT ON COLUMN question.gd_teacher_comment.reviewer_id IS '教师ID';
COMMENT ON COLUMN question.gd_teacher_comment.comment_type IS '点评类型TEXT/VOICE';
COMMENT ON COLUMN question.gd_teacher_comment.content_ref IS '点评内容或资源引用';
COMMENT ON COLUMN question.gd_teacher_comment.tenant_id IS '租户ID';
COMMENT ON COLUMN question.gd_teacher_comment.created_at IS '创建时间';
DROP TABLE IF EXISTS question.gd_explanation_dimension_score CASCADE;
DROP TABLE IF EXISTS question.gd_explanation_assessment CASCADE;
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),
audio_file_id VARCHAR(64) NOT NULL,
duration_seconds INTEGER,
transcript_text TEXT,
submission_status VARCHAR(16) NOT NULL DEFAULT 'SUBMITTED',
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_explanation_submission_status
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
FOREIGN KEY (source_submission_id) REFERENCES question.hw_submission(submission_id),
CONSTRAINT fk_gd_explanation_submission_source_answer
FOREIGN KEY (source_answer_id) REFERENCES question.hw_submission_answer(answer_id),
CONSTRAINT fk_gd_explanation_submission_audio_file
FOREIGN KEY (audio_file_id) REFERENCES upms.tb_sys_file(file_id)
);
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';
COMMENT ON COLUMN question.gd_explanation_submission.audio_file_id IS '讲解音频文件ID';
COMMENT ON COLUMN question.gd_explanation_submission.duration_seconds IS '时长(秒)';
COMMENT ON COLUMN question.gd_explanation_submission.transcript_text IS '转写文本';
COMMENT ON COLUMN question.gd_explanation_submission.submission_status IS '提交状态';
COMMENT ON COLUMN question.gd_explanation_submission.tenant_id IS '租户ID';
COMMENT ON COLUMN question.gd_explanation_submission.created_at IS '创建时间';
COMMENT ON COLUMN question.gd_explanation_submission.updated_at IS '更新时间';
CREATE TABLE IF NOT EXISTS question.gd_explanation_assessment (
assessment_id VARCHAR(64) PRIMARY KEY,
explanation_id VARCHAR(64) NOT NULL UNIQUE,
evaluator_type VARCHAR(16) NOT NULL DEFAULT 'AI',
evaluator_id VARCHAR(64),
total_score NUMERIC(8,2),
pass_status VARCHAR(16) NOT NULL DEFAULT 'PENDING',
improvement_suggestion TEXT,
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_explanation_assessment_evaluator_type
CHECK (evaluator_type IN ('AI', 'TEACHER', 'HYBRID')),
CONSTRAINT chk_gd_explanation_assessment_pass_status
CHECK (pass_status IN ('PENDING', 'PASSED', 'FAILED')),
CONSTRAINT fk_gd_explanation_assessment_explanation
FOREIGN KEY (explanation_id) REFERENCES question.gd_explanation_submission(explanation_id) ON DELETE CASCADE,
CONSTRAINT fk_gd_explanation_assessment_evaluator
FOREIGN KEY (evaluator_id) REFERENCES upms.tb_sys_user(user_id)
);
COMMENT ON TABLE question.gd_explanation_assessment IS '费曼讲解评估汇总表';
COMMENT ON COLUMN question.gd_explanation_assessment.assessment_id IS '评估ID';
COMMENT ON COLUMN question.gd_explanation_assessment.explanation_id IS '讲解提交ID';
COMMENT ON COLUMN question.gd_explanation_assessment.evaluator_type IS '评估类型';
COMMENT ON COLUMN question.gd_explanation_assessment.evaluator_id IS '评估人ID';
COMMENT ON COLUMN question.gd_explanation_assessment.total_score IS '总分';
COMMENT ON COLUMN question.gd_explanation_assessment.pass_status IS '通过状态';
COMMENT ON COLUMN question.gd_explanation_assessment.improvement_suggestion IS '改进建议';
COMMENT ON COLUMN question.gd_explanation_assessment.tenant_id IS '租户ID';
COMMENT ON COLUMN question.gd_explanation_assessment.created_at IS '创建时间';
COMMENT ON COLUMN question.gd_explanation_assessment.updated_at IS '更新时间';
CREATE TABLE IF NOT EXISTS question.gd_explanation_dimension_score (
dimension_score_id VARCHAR(64) PRIMARY KEY,
assessment_id VARCHAR(64) NOT NULL,
dimension_code VARCHAR(32) NOT NULL,
dimension_name VARCHAR(64) NOT NULL,
score NUMERIC(8,2) NOT NULL DEFAULT 0,
weight NUMERIC(6,4) NOT NULL DEFAULT 1.0000,
comment_text TEXT,
tenant_id VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chk_gd_explanation_dimension_score_weight
CHECK (weight > 0 AND weight <= 1),
CONSTRAINT fk_gd_explanation_dimension_score_assessment
FOREIGN KEY (assessment_id) REFERENCES question.gd_explanation_assessment(assessment_id) ON DELETE CASCADE
);
COMMENT ON TABLE question.gd_explanation_dimension_score IS '费曼讲解维度评分表';
COMMENT ON COLUMN question.gd_explanation_dimension_score.dimension_score_id IS '维度评分ID';
COMMENT ON COLUMN question.gd_explanation_dimension_score.assessment_id IS '评估ID';
COMMENT ON COLUMN question.gd_explanation_dimension_score.dimension_code IS '维度编码';
COMMENT ON COLUMN question.gd_explanation_dimension_score.dimension_name IS '维度名称';
COMMENT ON COLUMN question.gd_explanation_dimension_score.score IS '维度得分';
COMMENT ON COLUMN question.gd_explanation_dimension_score.weight IS '维度权重';
COMMENT ON COLUMN question.gd_explanation_dimension_score.comment_text IS '维度评语';
COMMENT ON COLUMN question.gd_explanation_dimension_score.tenant_id IS '租户ID';
COMMENT ON COLUMN question.gd_explanation_dimension_score.created_at IS '创建时间';
CREATE INDEX IF NOT EXISTS idx_gd_grading_task_submission
ON question.gd_grading_task(submission_id, task_status);
CREATE INDEX IF NOT EXISTS idx_gd_grading_task_paper_status
ON question.gd_grading_task(paper_id, task_status, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_gd_answer_grade_task_status
ON question.gd_answer_grade(grading_task_id, grade_status);
CREATE INDEX IF NOT EXISTS idx_gd_answer_grade_paper_status
ON question.gd_answer_grade(paper_id, grade_status, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_gd_answer_error_rel_answer
ON question.gd_answer_error_rel(answer_id);
CREATE INDEX IF NOT EXISTS idx_gd_answer_kp_analysis_kp
ON question.gd_answer_kp_analysis(kp_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_gd_answer_kp_analysis_task
ON question.gd_answer_kp_analysis(grading_task_id, kp_id);
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);