задачи 2 и 8
This commit is contained in:
@@ -2182,3 +2182,323 @@ ALTER TABLE teacher_department_assignments
|
||||
COMMENT ON CONSTRAINT ex_teacher_primary_department_no_overlap
|
||||
ON teacher_department_assignments
|
||||
IS 'У преподавателя не могут пересекаться периоды двух основных кафедр';
|
||||
|
||||
-- ==========================================
|
||||
-- Отсутствия преподавателей и мастер замены
|
||||
-- ==========================================
|
||||
CREATE TABLE teacher_absences (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
teacher_id BIGINT NOT NULL REFERENCES users(id),
|
||||
start_date DATE NOT NULL,
|
||||
end_date DATE NOT NULL,
|
||||
reason TEXT NOT NULL,
|
||||
status VARCHAR(20) NOT NULL,
|
||||
requested_by BIGINT REFERENCES users(id),
|
||||
reviewed_by BIGINT REFERENCES users(id),
|
||||
review_comment TEXT,
|
||||
reviewed_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT chk_teacher_absences_period CHECK (end_date >= start_date),
|
||||
CONSTRAINT chk_teacher_absences_reason CHECK (length(trim(reason)) BETWEEN 1 AND 2000),
|
||||
CONSTRAINT chk_teacher_absences_status CHECK (
|
||||
status IN ('PENDING', 'APPROVED', 'REJECTED', 'RESOLVED', 'CANCELLED')
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_teacher_absences_teacher_period
|
||||
ON teacher_absences(teacher_id, start_date, end_date);
|
||||
|
||||
CREATE INDEX idx_teacher_absences_status
|
||||
ON teacher_absences(status);
|
||||
|
||||
CREATE TABLE teacher_absence_decisions (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
absence_id BIGINT NOT NULL REFERENCES teacher_absences(id) ON DELETE CASCADE,
|
||||
base_rule_slot_id BIGINT NOT NULL REFERENCES schedule_rule_slots(id),
|
||||
lesson_date DATE NOT NULL,
|
||||
resolution VARCHAR(30) NOT NULL,
|
||||
decision_status VARCHAR(20) NOT NULL,
|
||||
schedule_override_id BIGINT REFERENCES schedule_overrides(id) ON DELETE SET NULL,
|
||||
comment TEXT,
|
||||
decided_by BIGINT REFERENCES users(id),
|
||||
decided_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT uq_teacher_absence_decision_lesson UNIQUE (
|
||||
absence_id,
|
||||
base_rule_slot_id,
|
||||
lesson_date
|
||||
),
|
||||
CONSTRAINT chk_teacher_absence_decision_resolution CHECK (
|
||||
resolution IN ('REPLACE_TEACHER', 'MOVE_TIME', 'CHANGE_CLASSROOM', 'CANCEL', 'REJECT')
|
||||
),
|
||||
CONSTRAINT chk_teacher_absence_decision_status CHECK (
|
||||
decision_status IN ('APPLIED', 'REJECTED')
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_teacher_absence_decisions_absence
|
||||
ON teacher_absence_decisions(absence_id, lesson_date);
|
||||
|
||||
COMMENT ON TABLE teacher_absences IS 'Временные отсутствия преподавателей с согласованием кафедрой';
|
||||
COMMENT ON COLUMN teacher_absences.status IS 'PENDING, APPROVED, REJECTED, RESOLVED или CANCELLED';
|
||||
COMMENT ON TABLE teacher_absence_decisions IS 'Журнал применённых и отклонённых решений мастера замены';
|
||||
COMMENT ON COLUMN teacher_absence_decisions.resolution IS 'Выбранный способ разрешения затронутого занятия';
|
||||
|
||||
-- ==========================================
|
||||
-- Пожелания и заявки преподавателей
|
||||
-- ==========================================
|
||||
CREATE TABLE teacher_preferences (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
teacher_id BIGINT NOT NULL REFERENCES users(id),
|
||||
semester_id BIGINT NOT NULL REFERENCES semesters(id),
|
||||
preference_type VARCHAR(30) NOT NULL,
|
||||
day_of_week SMALLINT,
|
||||
time_slot_id BIGINT REFERENCES time_slots(id),
|
||||
preference_date DATE,
|
||||
comment TEXT,
|
||||
status VARCHAR(20) NOT NULL,
|
||||
requested_by BIGINT REFERENCES users(id),
|
||||
reviewed_by BIGINT REFERENCES users(id),
|
||||
review_comment TEXT,
|
||||
reviewed_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT chk_teacher_preferences_type CHECK (
|
||||
preference_type IN (
|
||||
'HARD_UNAVAILABLE',
|
||||
'SOFT_PREFERRED',
|
||||
'SOFT_UNWANTED',
|
||||
'CONSECUTIVE',
|
||||
'NO_GAPS'
|
||||
)
|
||||
),
|
||||
CONSTRAINT chk_teacher_preferences_status CHECK (
|
||||
status IN ('PENDING', 'APPROVED', 'REJECTED', 'CANCELLED')
|
||||
),
|
||||
CONSTRAINT chk_teacher_preferences_day CHECK (
|
||||
day_of_week IS NULL OR day_of_week BETWEEN 1 AND 7
|
||||
),
|
||||
CONSTRAINT chk_teacher_preferences_payload CHECK (
|
||||
(
|
||||
preference_type = 'HARD_UNAVAILABLE'
|
||||
AND (
|
||||
(preference_date IS NOT NULL AND day_of_week IS NULL AND time_slot_id IS NULL)
|
||||
OR
|
||||
(preference_date IS NULL AND day_of_week IS NOT NULL AND time_slot_id IS NOT NULL)
|
||||
)
|
||||
)
|
||||
OR
|
||||
(
|
||||
preference_type IN ('SOFT_PREFERRED', 'SOFT_UNWANTED')
|
||||
AND preference_date IS NULL
|
||||
AND day_of_week IS NOT NULL
|
||||
AND time_slot_id IS NOT NULL
|
||||
)
|
||||
OR
|
||||
(
|
||||
preference_type IN ('CONSECUTIVE', 'NO_GAPS')
|
||||
AND preference_date IS NULL
|
||||
AND day_of_week IS NULL
|
||||
AND time_slot_id IS NULL
|
||||
)
|
||||
),
|
||||
CONSTRAINT chk_teacher_preferences_comment CHECK (
|
||||
comment IS NULL OR length(trim(comment)) BETWEEN 1 AND 2000
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_teacher_preferences_teacher_semester
|
||||
ON teacher_preferences(teacher_id, semester_id, status);
|
||||
|
||||
CREATE INDEX idx_teacher_preferences_slot
|
||||
ON teacher_preferences(semester_id, day_of_week, time_slot_id)
|
||||
WHERE status = 'APPROVED';
|
||||
|
||||
CREATE INDEX idx_teacher_preferences_date
|
||||
ON teacher_preferences(teacher_id, preference_date)
|
||||
WHERE status = 'APPROVED' AND preference_date IS NOT NULL;
|
||||
|
||||
CREATE TABLE teacher_change_requests (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
teacher_id BIGINT NOT NULL REFERENCES users(id),
|
||||
base_rule_slot_id BIGINT NOT NULL REFERENCES schedule_rule_slots(id),
|
||||
lesson_date DATE NOT NULL,
|
||||
request_type VARCHAR(30) NOT NULL,
|
||||
target_lesson_date DATE,
|
||||
requested_time_slot_id BIGINT REFERENCES time_slots(id),
|
||||
requested_classroom_id BIGINT REFERENCES classrooms(id),
|
||||
reason TEXT NOT NULL,
|
||||
status VARCHAR(20) NOT NULL,
|
||||
reviewed_by BIGINT REFERENCES users(id),
|
||||
review_comment TEXT,
|
||||
reviewed_at TIMESTAMPTZ,
|
||||
applied_override_id BIGINT REFERENCES schedule_overrides(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT chk_teacher_change_requests_type CHECK (
|
||||
request_type IN ('MOVE', 'CHANGE_CLASSROOM', 'CANCEL')
|
||||
),
|
||||
CONSTRAINT chk_teacher_change_requests_status CHECK (
|
||||
status IN ('PENDING', 'APPROVED', 'REJECTED', 'CANCELLED')
|
||||
),
|
||||
CONSTRAINT chk_teacher_change_requests_reason CHECK (
|
||||
length(trim(reason)) BETWEEN 1 AND 2000
|
||||
),
|
||||
CONSTRAINT chk_teacher_change_requests_payload CHECK (
|
||||
(
|
||||
request_type = 'MOVE'
|
||||
AND requested_time_slot_id IS NOT NULL
|
||||
AND requested_classroom_id IS NULL
|
||||
)
|
||||
OR
|
||||
(
|
||||
request_type = 'CHANGE_CLASSROOM'
|
||||
AND target_lesson_date IS NULL
|
||||
AND requested_time_slot_id IS NULL
|
||||
AND requested_classroom_id IS NOT NULL
|
||||
)
|
||||
OR
|
||||
(
|
||||
request_type = 'CANCEL'
|
||||
AND target_lesson_date IS NULL
|
||||
AND requested_time_slot_id IS NULL
|
||||
AND requested_classroom_id IS NULL
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_teacher_change_requests_teacher
|
||||
ON teacher_change_requests(teacher_id, created_at DESC);
|
||||
|
||||
CREATE INDEX idx_teacher_change_requests_status
|
||||
ON teacher_change_requests(status, created_at);
|
||||
|
||||
CREATE UNIQUE INDEX uq_teacher_change_requests_pending_lesson
|
||||
ON teacher_change_requests(base_rule_slot_id, lesson_date)
|
||||
WHERE status = 'PENDING';
|
||||
|
||||
CREATE TABLE teacher_change_request_history (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
request_id BIGINT NOT NULL REFERENCES teacher_change_requests(id) ON DELETE CASCADE,
|
||||
status VARCHAR(20) NOT NULL,
|
||||
comment TEXT,
|
||||
actor_id BIGINT REFERENCES users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT chk_teacher_change_request_history_status CHECK (
|
||||
status IN ('PENDING', 'APPROVED', 'REJECTED', 'CANCELLED')
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_teacher_change_request_history_request
|
||||
ON teacher_change_request_history(request_id, created_at, id);
|
||||
|
||||
COMMENT ON TABLE teacher_preferences IS 'Согласуемые ограничения и пожелания преподавателей на семестр';
|
||||
COMMENT ON COLUMN teacher_preferences.preference_type IS 'Строгая недоступность, мягкое пожелание или пожелание к компактности расписания';
|
||||
COMMENT ON TABLE teacher_change_requests IS 'Заявки преподавателей на перенос, смену аудитории или отмену опубликованного занятия';
|
||||
COMMENT ON TABLE teacher_change_request_history IS 'Неизменяемая история статусов и комментариев по заявке преподавателя';
|
||||
|
||||
-- ==========================================
|
||||
-- Черновики, версии, публикация и аудит расписания
|
||||
-- ==========================================
|
||||
CREATE TABLE schedule_versions (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
semester_id BIGINT NOT NULL REFERENCES semesters(id) ON DELETE CASCADE,
|
||||
version_number INT NOT NULL,
|
||||
name VARCHAR(160) NOT NULL,
|
||||
status VARCHAR(20) NOT NULL,
|
||||
based_on_version_id BIGINT REFERENCES schedule_versions(id) ON DELETE SET NULL,
|
||||
restored_from_version_id BIGINT REFERENCES schedule_versions(id) ON DELETE SET NULL,
|
||||
change_reason TEXT,
|
||||
created_by BIGINT REFERENCES users(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
published_by BIGINT REFERENCES users(id),
|
||||
published_at TIMESTAMPTZ,
|
||||
archived_at TIMESTAMPTZ,
|
||||
CONSTRAINT uq_schedule_versions_number UNIQUE (semester_id, version_number),
|
||||
CONSTRAINT chk_schedule_versions_number CHECK (version_number > 0),
|
||||
CONSTRAINT chk_schedule_versions_name CHECK (length(trim(name)) BETWEEN 1 AND 160),
|
||||
CONSTRAINT chk_schedule_versions_status CHECK (status IN ('DRAFT', 'PUBLISHED', 'ARCHIVED')),
|
||||
CONSTRAINT chk_schedule_versions_publish_fields CHECK (
|
||||
status <> 'PUBLISHED' OR published_at IS NOT NULL
|
||||
)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX uq_schedule_versions_published_semester
|
||||
ON schedule_versions(semester_id)
|
||||
WHERE status = 'PUBLISHED';
|
||||
|
||||
CREATE INDEX idx_schedule_versions_semester_history
|
||||
ON schedule_versions(semester_id, version_number DESC);
|
||||
|
||||
INSERT INTO schedule_versions (
|
||||
semester_id,
|
||||
version_number,
|
||||
name,
|
||||
status,
|
||||
created_at,
|
||||
published_at
|
||||
)
|
||||
SELECT
|
||||
semester.id,
|
||||
1,
|
||||
'Опубликованная версия 1',
|
||||
'PUBLISHED',
|
||||
CURRENT_TIMESTAMP,
|
||||
CURRENT_TIMESTAMP
|
||||
FROM semesters semester;
|
||||
|
||||
ALTER TABLE schedule_rules
|
||||
ADD COLUMN schedule_version_id BIGINT;
|
||||
|
||||
UPDATE schedule_rules rule
|
||||
SET schedule_version_id = version.id,
|
||||
version_group_id = COALESCE(rule.version_group_id, rule.id)
|
||||
FROM schedule_versions version
|
||||
WHERE version.semester_id = rule.semester_id
|
||||
AND version.status = 'PUBLISHED';
|
||||
|
||||
ALTER TABLE schedule_rules
|
||||
ALTER COLUMN schedule_version_id SET NOT NULL,
|
||||
ADD CONSTRAINT fk_schedule_rules_version
|
||||
FOREIGN KEY (schedule_version_id) REFERENCES schedule_versions(id) ON DELETE CASCADE;
|
||||
|
||||
CREATE INDEX idx_schedule_rules_version
|
||||
ON schedule_rules(schedule_version_id, status);
|
||||
|
||||
CREATE UNIQUE INDEX uq_schedule_rules_version_group
|
||||
ON schedule_rules(schedule_version_id, version_group_id)
|
||||
WHERE version_group_id IS NOT NULL;
|
||||
|
||||
CREATE TABLE schedule_version_history (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
version_id BIGINT NOT NULL REFERENCES schedule_versions(id) ON DELETE CASCADE,
|
||||
action VARCHAR(30) NOT NULL,
|
||||
actor_id BIGINT REFERENCES users(id),
|
||||
reason TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT chk_schedule_version_history_action CHECK (
|
||||
action IN ('CREATED', 'PUBLISHED', 'ARCHIVED', 'RESTORED')
|
||||
),
|
||||
CONSTRAINT chk_schedule_version_history_reason CHECK (
|
||||
reason IS NULL OR length(trim(reason)) BETWEEN 1 AND 2000
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_schedule_version_history_version
|
||||
ON schedule_version_history(version_id, created_at DESC, id DESC);
|
||||
|
||||
INSERT INTO schedule_version_history (version_id, action, reason, created_at)
|
||||
SELECT id, 'CREATED', 'Начальная версия расписания', created_at
|
||||
FROM schedule_versions;
|
||||
|
||||
INSERT INTO schedule_version_history (version_id, action, reason, created_at)
|
||||
SELECT id, 'PUBLISHED', 'Начальная публикация расписания', published_at
|
||||
FROM schedule_versions;
|
||||
|
||||
COMMENT ON TABLE schedule_versions IS 'Изолированные черновики и опубликованные версии правил расписания';
|
||||
COMMENT ON COLUMN schedule_versions.status IS 'DRAFT, PUBLISHED или ARCHIVED';
|
||||
COMMENT ON COLUMN schedule_versions.based_on_version_id IS 'Версия, из которой был скопирован черновик';
|
||||
COMMENT ON COLUMN schedule_versions.restored_from_version_id IS 'Предыдущая опубликованная версия при восстановлении';
|
||||
COMMENT ON COLUMN schedule_rules.schedule_version_id IS 'Версия расписания, которой принадлежит правило';
|
||||
COMMENT ON COLUMN schedule_rules.version_group_id IS 'Стабильный идентификатор правила между версиями';
|
||||
COMMENT ON TABLE schedule_version_history IS 'Неизменяемый журнал создания, публикации, архивации и восстановления версий';
|
||||
|
||||
Reference in New Issue
Block a user