2026-04-16 11:30:30 +08:00
|
|
|
|
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,
|
2026-04-16 15:46:29 +08:00
|
|
|
|
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),
|
2026-04-16 11:30:30 +08:00
|
|
|
|
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 '计划日期';
|
2026-04-16 15:46:29 +08:00
|
|
|
|
COMMENT ON COLUMN question.gd_review_plan.plan_stage IS '阶段(E1-E10/CUSTOM)';
|
2026-04-16 11:30:30 +08:00
|
|
|
|
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 '创建时间';
|
2026-04-16 15:46:29 +08:00
|
|
|
|
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 '创建时间';
|
2026-04-16 11:30:30 +08:00
|
|
|
|
|
|
|
|
|
|
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,
|
2026-04-16 15:46:29 +08:00
|
|
|
|
question_id VARCHAR(64) NOT NULL,
|
2026-04-16 11:30:30 +08:00
|
|
|
|
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),
|
2026-04-16 15:46:29 +08:00
|
|
|
|
CONSTRAINT fk_gd_explanation_submission_question
|
|
|
|
|
|
FOREIGN KEY (question_id) REFERENCES question.hw_question_item(question_id),
|
2026-04-16 11:30:30 +08:00
|
|
|
|
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';
|
2026-04-16 15:46:29 +08:00
|
|
|
|
COMMENT ON COLUMN question.gd_explanation_submission.question_id IS '绑定题目ID(必填)';
|
2026-04-16 11:30:30 +08:00
|
|
|
|
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);
|
2026-04-16 15:46:29 +08:00
|
|
|
|
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);
|
2026-04-16 11:30:30 +08:00
|
|
|
|
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);
|
2026-04-16 15:46:29 +08:00
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_gd_explanation_submission_question
|
|
|
|
|
|
ON question.gd_explanation_submission(question_id, created_at DESC);
|
2026-04-16 11:30:30 +08:00
|
|
|
|
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);
|
2026-04-16 15:46:29 +08:00
|
|
|
|
|
|
|
|
|
|
-- 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);
|