-- ============================================================================== -- RailwaySystem QA Bootstrap: 02_training_v2_schema.sql (权威最终修复版) -- 目标数据库: railwaysystem_fusion_qa_20261013 (schema: public) -- 依据来源: -- 1. 融合工程最新真实 Java 实体类 (TrainAccommodation, TrainEnrollment, TrainResource, etc.) -- 2. 最新 MyBatis Mapper XML (TrainAccommodationMapper, TrainClassroomInfoMapper, etc.) -- 3. 权威迁移与结构基线: -- - sql/train/v2/rollback/000_phase05_exact_schema.sql -- - sql/train/v2/002_create_new_tables.sql -- - sql/train/v2/003_alter_master_tables.sql -- - sql/train/v2/004_rebuild_business_tables.sql -- - sql/train/v2/005_constraints.sql -- 覆盖范围: 全部 23 张培训业务表、完整列定义、主外键、唯一约束与业务索引 -- 安全设计: -- 1. 纯净 DDL: 杜绝 DROP TABLE / TRUNCATE / CTAS,采用 CREATE TABLE IF NOT EXISTS -- 2. 外键统一使用 ON DELETE NO ACTION (杜绝 CASCADE) -- 3. 修复 train_accommodation(enrollment_id) 全局唯一冲突,采用部分唯一索引支持退宿与重新入住 -- 4. 字段名严格对齐 Mapper SQL (如 accommodation_id, class_room_id, resource_id, student_id, weight, door_auth_id) -- 5. 显式限定 search_path 至 public -- ============================================================================== BEGIN; SET search_path TO public, pg_catalog, sys_catalog; -- ============================================================================== -- 第一部分: 培训主数据表 (8 张) -- ============================================================================== -- 1. 教师主档表 (包含 person_no 门禁人员编号) CREATE TABLE IF NOT EXISTS public.train_teacher ( t_id VARCHAR(32) NOT NULL, t_name VARCHAR(50) NOT NULL, person_no VARCHAR(32), t_gender CHAR(1), t_birth DATE, t_department VARCHAR(50), org_name VARCHAR(100), job_title VARCHAR(50), t_title VARCHAR(30), t_phone VARCHAR(20), t_email VARCHAR(100), user_id BIGINT, avatar VARCHAR(255), major VARCHAR(100), courses VARCHAR(255), start_teaching DATE, status SMALLINT DEFAULT 1, create_by VARCHAR(64), create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, update_by VARCHAR(64), update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, remark VARCHAR(500), del_flag CHAR(1) NOT NULL DEFAULT '0', CONSTRAINT pk_train_teacher PRIMARY KEY (t_id) ); COMMENT ON TABLE public.train_teacher IS '教师主档表'; COMMENT ON COLUMN public.train_teacher.person_no IS '教师门禁/人脸人员编号 (对接 IoT 门禁系统)'; -- 2. 学员主档表 (包含 person_no, id_card_ciphertext, face_ref) CREATE TABLE IF NOT EXISTS public.train_student ( s_id VARCHAR(32) NOT NULL, s_name VARCHAR(50) NOT NULL, person_no VARCHAR(32), s_gender CHAR(1), s_birth DATE, org_name VARCHAR(100) NOT NULL, s_phone VARCHAR(20), s_email VARCHAR(100), id_card_ciphertext TEXT, id_card_hash VARCHAR(128), id_card_last4 CHAR(4), face_ref VARCHAR(255), source_type SMALLINT NOT NULL DEFAULT 1, user_id BIGINT, avatar VARCHAR(255), duty_level VARCHAR(64), status SMALLINT NOT NULL DEFAULT 1, create_by VARCHAR(64), create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, update_by VARCHAR(64), update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, remark VARCHAR(500), del_flag CHAR(1) NOT NULL DEFAULT '0', CONSTRAINT pk_train_student PRIMARY KEY (s_id) ); COMMENT ON TABLE public.train_student IS '学员主档表'; COMMENT ON COLUMN public.train_student.person_no IS '学员门禁/人脸人员编号 (对接 IoT 门禁系统,如 AA000001)'; COMMENT ON COLUMN public.train_student.id_card_ciphertext IS '身份证号加密密文'; COMMENT ON COLUMN public.train_student.face_ref IS '人脸特征或图片引用路径'; -- 3. 课程主表 (包含 resource_id 关联教学资源) CREATE TABLE IF NOT EXISTS public.train_course ( c_id VARCHAR(32) NOT NULL, c_name VARCHAR(100) NOT NULL, c_code VARCHAR(50), c_type SMALLINT, c_duration INTEGER, teaching_objective TEXT, course_content TEXT, resource_id VARCHAR(32), status SMALLINT NOT NULL DEFAULT 1, create_by VARCHAR(64), create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, update_by VARCHAR(64), update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, remark VARCHAR(500), del_flag CHAR(1) NOT NULL DEFAULT '0', CONSTRAINT pk_train_course PRIMARY KEY (c_id) ); COMMENT ON TABLE public.train_course IS '课程标准库主表'; -- 4. 教室信息表 (主键 class_room_id 严格对齐 Mapper 与 Phase 0.5 基线) CREATE TABLE IF NOT EXISTS public.train_classroom_info ( class_room_id VARCHAR(32) NOT NULL, room_name VARCHAR(50) NOT NULL, room_location VARCHAR(100), capacity INTEGER NOT NULL DEFAULT 30, seat_arrange VARCHAR(100), multimedia SMALLINT DEFAULT 0, projector SMALLINT DEFAULT 0, room_status SMALLINT NOT NULL DEFAULT 1, create_by VARCHAR(64), create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, update_by VARCHAR(64), update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, remark VARCHAR(500), del_flag CHAR(1) NOT NULL DEFAULT '0', CONSTRAINT pk_train_classroom_info PRIMARY KEY (class_room_id), CONSTRAINT ck_classroom_capacity CHECK (capacity > 0) ); COMMENT ON TABLE public.train_classroom_info IS '教室信息表'; COMMENT ON COLUMN public.train_classroom_info.class_room_id IS '教室唯一ID (对齐 TrainClassroomInfoMapper)'; -- 5. 实训场地信息表 CREATE TABLE IF NOT EXISTS public.train_training_site ( site_id VARCHAR(32) NOT NULL, site_name VARCHAR(50) NOT NULL, site_location VARCHAR(100), capacity INTEGER NOT NULL DEFAULT 20, site_device VARCHAR(200), major VARCHAR(100), seat_arrange VARCHAR(100), site_status SMALLINT NOT NULL DEFAULT 1, create_by VARCHAR(64), create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, update_by VARCHAR(64), update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, remark VARCHAR(500), del_flag CHAR(1) NOT NULL DEFAULT '0', CONSTRAINT pk_train_training_site PRIMARY KEY (site_id), CONSTRAINT ck_site_capacity CHECK (capacity > 0) ); COMMENT ON TABLE public.train_training_site IS '实训场地信息表'; -- 6. 电子班牌终端表 (包含 api_endpoint) CREATE TABLE IF NOT EXISTS public.train_electronic_board ( board_id VARCHAR(32) NOT NULL, board_sn VARCHAR(50) NOT NULL, board_name VARCHAR(50) NOT NULL, bind_classroom_id VARCHAR(32), board_ip VARCHAR(40), api_endpoint VARCHAR(255), support_checkin_type VARCHAR(100), board_status SMALLINT NOT NULL DEFAULT 1, last_heartbeat TIMESTAMP, create_by VARCHAR(64), create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, update_by VARCHAR(64), update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, remark VARCHAR(500), del_flag CHAR(1) NOT NULL DEFAULT '0', CONSTRAINT pk_train_electronic_board PRIMARY KEY (board_id) ); COMMENT ON TABLE public.train_electronic_board IS '电子班牌终端表'; -- 7. 宿舍房间信息表 (包含 lock_device_sn) CREATE TABLE IF NOT EXISTS public.train_dormitory_info ( dorm_id VARCHAR(32) NOT NULL, dorm_building VARCHAR(50) NOT NULL, dorm_floor VARCHAR(20) NOT NULL, dorm_room VARCHAR(20) NOT NULL, capacity INTEGER NOT NULL DEFAULT 4, gender_type SMALLINT NOT NULL DEFAULT 1, dorm_status SMALLINT NOT NULL DEFAULT 1, lock_device_sn VARCHAR(64), create_by VARCHAR(64), create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, update_by VARCHAR(64), update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, remark VARCHAR(500), del_flag CHAR(1) NOT NULL DEFAULT '0', CONSTRAINT pk_train_dormitory_info PRIMARY KEY (dorm_id), CONSTRAINT ck_dorm_capacity CHECK (capacity > 0), CONSTRAINT ck_dorm_gender CHECK (gender_type IN (1, 2)) ); COMMENT ON TABLE public.train_dormitory_info IS '宿舍房间信息表'; -- 8. 培训班级计划表 (字段与 TrainClassPlanMapper 严格对齐) CREATE TABLE IF NOT EXISTS public.train_class_plan ( plan_id VARCHAR(32) NOT NULL, plan_name VARCHAR(100) NOT NULL, start_time TIMESTAMP, end_time TIMESTAMP, report_deadline TIMESTAMP, train_address VARCHAR(255), recruit_num INTEGER DEFAULT 0, apply_condition TEXT, plan_status SMALLINT NOT NULL DEFAULT 0, create_by VARCHAR(64), create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, update_by VARCHAR(64), update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, remark VARCHAR(500), del_flag CHAR(1) NOT NULL DEFAULT '0', CONSTRAINT pk_train_class_plan PRIMARY KEY (plan_id) ); COMMENT ON TABLE public.train_class_plan IS '培训班级计划表'; -- ============================================================================== -- 第二部分: 教学与参训业务表 (8 张) -- ============================================================================== -- 9. 统一教学资源主表 (主键 resource_id 严格对齐 TrainResourceMapper) CREATE TABLE IF NOT EXISTS public.train_resource ( resource_id VARCHAR(32) NOT NULL, resource_name VARCHAR(150) NOT NULL, resource_type SMALLINT NOT NULL, resource_url TEXT NOT NULL, description TEXT, status SMALLINT NOT NULL DEFAULT 1, create_by VARCHAR(64), create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, update_by VARCHAR(64), update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, remark VARCHAR(500), del_flag CHAR(1) NOT NULL DEFAULT '0', CONSTRAINT pk_train_resource PRIMARY KEY (resource_id) ); COMMENT ON TABLE public.train_resource IS '统一教学资源主表'; -- 10. 课程与教学资源关联表 (外键 resource_id 对齐 TrainCourseResourceMapper) CREATE TABLE IF NOT EXISTS public.train_course_resource ( id BIGINT GENERATED BY DEFAULT AS IDENTITY, c_id VARCHAR(32) NOT NULL, resource_id VARCHAR(32) NOT NULL, sort_no INTEGER DEFAULT 0, create_by VARCHAR(64), create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT pk_train_course_resource PRIMARY KEY (id) ); COMMENT ON TABLE public.train_course_resource IS '课程与教学资源关联表'; -- 11. 培训班与课程关联表 (主键 plan_course_id) CREATE TABLE IF NOT EXISTS public.train_plan_course ( plan_course_id BIGINT GENERATED BY DEFAULT AS IDENTITY, plan_id VARCHAR(32) NOT NULL, c_id VARCHAR(32) NOT NULL, sort_no INTEGER DEFAULT 0, create_by VARCHAR(64), create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, remark VARCHAR(500), CONSTRAINT pk_train_plan_course PRIMARY KEY (plan_course_id) ); COMMENT ON TABLE public.train_plan_course IS '培训班与课程关联表'; -- 12. 学员参训报名表 (外键 student_id 对齐 TrainEnrollmentMapper) CREATE TABLE IF NOT EXISTS public.train_enrollment ( enrollment_id BIGINT GENERATED BY DEFAULT AS IDENTITY, plan_id VARCHAR(32) NOT NULL, student_id VARCHAR(32) NOT NULL, source_type SMALLINT NOT NULL DEFAULT 1, enrollment_status SMALLINT NOT NULL DEFAULT 0, accommodation_required SMALLINT NOT NULL DEFAULT 0, join_time TIMESTAMP, complete_time TIMESTAMP, create_by VARCHAR(64), create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, update_by VARCHAR(64), update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, remark VARCHAR(500), CONSTRAINT pk_train_enrollment PRIMARY KEY (enrollment_id) ); COMMENT ON TABLE public.train_enrollment IS '学员参训报名档案表'; COMMENT ON COLUMN public.train_enrollment.student_id IS '学员ID (关联 train_student.s_id)'; -- 13. 宿舍住宿安排表 (主键 accommodation_id, accommodation_status, assigned_time) -- 依据 sql/train/v2/002_create_new_tables.sql 行 136-156 与 TrainAccommodationMapper CREATE TABLE IF NOT EXISTS public.train_accommodation ( accommodation_id BIGINT GENERATED BY DEFAULT AS IDENTITY, enrollment_id BIGINT NOT NULL, dorm_id VARCHAR(32) NOT NULL, bed_no VARCHAR(20), assigned_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, accommodation_status SMALLINT NOT NULL DEFAULT 0, create_by VARCHAR(64), create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, update_by VARCHAR(64), update_time TIMESTAMP, remark VARCHAR(500), CONSTRAINT pk_train_accommodation PRIMARY KEY (accommodation_id) ); COMMENT ON TABLE public.train_accommodation IS '宿舍住宿安排记录表'; COMMENT ON COLUMN public.train_accommodation.accommodation_id IS '住宿安排ID (自增主键)'; COMMENT ON COLUMN public.train_accommodation.accommodation_status IS '住宿状态 (0:预分配, 1:已入住, 2:已退宿)'; -- 14. 培训课表排课记录表 (外键关联 plan_course_id, class_room_id) CREATE TABLE IF NOT EXISTS public.train_lesson ( lesson_id BIGINT GENERATED BY DEFAULT AS IDENTITY, plan_course_id BIGINT NOT NULL, t_id VARCHAR(32) NOT NULL, start_time TIMESTAMP NOT NULL, end_time TIMESTAMP NOT NULL, venue_type SMALLINT NOT NULL, class_room_id VARCHAR(32), site_id VARCHAR(32), create_by VARCHAR(64), create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, update_by VARCHAR(64), update_time TIMESTAMP, remark VARCHAR(500), CONSTRAINT pk_train_lesson PRIMARY KEY (lesson_id), CONSTRAINT ck_lesson_time CHECK (end_time > start_time), CONSTRAINT ck_lesson_venue CHECK ( (venue_type = 1 AND class_room_id IS NOT NULL AND site_id IS NULL) OR (venue_type = 2 AND site_id IS NOT NULL AND class_room_id IS NULL) ) ); COMMENT ON TABLE public.train_lesson IS '培训课表排课记录表'; -- 15. 课程考核分项配置表 (列名 weight, score_item_id 对齐 002 及 TrainScoreInfoMapper) CREATE TABLE IF NOT EXISTS public.train_score_item ( score_item_id BIGINT GENERATED BY DEFAULT AS IDENTITY, plan_id VARCHAR(32) NOT NULL, plan_course_id BIGINT, item_name VARCHAR(100) NOT NULL, item_type SMALLINT NOT NULL DEFAULT 1, weight NUMERIC(5,2) NOT NULL DEFAULT 0.00, full_score NUMERIC(5,2) NOT NULL DEFAULT 100.00, sort_no INTEGER DEFAULT 0, create_by VARCHAR(64), create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, update_by VARCHAR(64), update_time TIMESTAMP, remark VARCHAR(500), CONSTRAINT pk_train_score_item PRIMARY KEY (score_item_id), CONSTRAINT ck_score_item_weight CHECK (weight >= 0.00 AND weight <= 100.00) ); COMMENT ON TABLE public.train_score_item IS '课程考核分项配置表'; COMMENT ON COLUMN public.train_score_item.weight IS '所占综合权重百分比 (对齐 002_create_new_tables)'; -- 16. 学员考核成绩明细表 (关联 score_item_id, enrollment_id) CREATE TABLE IF NOT EXISTS public.train_student_score ( score_id BIGINT GENERATED BY DEFAULT AS IDENTITY, enrollment_id BIGINT NOT NULL, score_item_id BIGINT NOT NULL, score NUMERIC(7,2) NOT NULL, source_type SMALLINT NOT NULL DEFAULT 1, source_system VARCHAR(100), source_event_id VARCHAR(100), import_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, create_by VARCHAR(64), create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, update_by VARCHAR(64), update_time TIMESTAMP, remark VARCHAR(500), CONSTRAINT pk_train_student_score PRIMARY KEY (score_id), CONSTRAINT ck_student_score_val CHECK (score >= 0.00) ); COMMENT ON TABLE public.train_student_score IS '学员考核成绩明细表'; -- ============================================================================== -- 第三部分: 业务过程扩展表 (5 张) -- ============================================================================== -- 17. 门禁授权同步过程表 (包含完整 DMS/物联桥接状态列) CREATE TABLE IF NOT EXISTS public.train_dorm_auth ( auth_id BIGINT GENERATED BY DEFAULT AS IDENTITY, accommodation_id BIGINT NOT NULL, auth_status VARCHAR(32) DEFAULT 'PENDING', credential_type VARCHAR(32) DEFAULT 'FACE', latest_operation_id BIGINT, operation_version BIGINT DEFAULT 0, auth_start TIMESTAMP, auth_end TIMESTAMP, face_sync_status SMALLINT NOT NULL DEFAULT 0, retry_count INTEGER NOT NULL DEFAULT 0, last_error VARCHAR(500), external_auth_id VARCHAR(100), last_sync_time TIMESTAMP, create_by VARCHAR(64), create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, update_by VARCHAR(64), update_time TIMESTAMP, remark VARCHAR(500), CONSTRAINT pk_train_dorm_auth PRIMARY KEY (auth_id) ); COMMENT ON TABLE public.train_dorm_auth IS '宿舍门禁授权同步过程表'; -- 18. 班级报到记录表 CREATE TABLE IF NOT EXISTS public.train_class_report ( report_id BIGINT GENERATED BY DEFAULT AS IDENTITY, enrollment_id BIGINT NOT NULL, report_way SMALLINT NOT NULL DEFAULT 1, report_device_sn VARCHAR(64), report_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, verify_result SMALLINT NOT NULL DEFAULT 1, verify_note VARCHAR(500), create_by VARCHAR(64), create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, remark VARCHAR(500), CONSTRAINT pk_train_class_report PRIMARY KEY (report_id) ); COMMENT ON TABLE public.train_class_report IS '班级报到记录表'; -- 19. 学员课次考勤打卡表 CREATE TABLE IF NOT EXISTS public.train_student_checkin ( checkin_id BIGINT GENERATED BY DEFAULT AS IDENTITY, enrollment_id BIGINT NOT NULL, lesson_id BIGINT NOT NULL, board_id VARCHAR(32), checkin_way SMALLINT NOT NULL DEFAULT 1, sign_in_time TIMESTAMP, sign_out_time TIMESTAMP, attendance_status SMALLINT NOT NULL DEFAULT 0, manual_flag SMALLINT NOT NULL DEFAULT 0, create_by VARCHAR(64), create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, update_by VARCHAR(64), update_time TIMESTAMP, remark VARCHAR(500), CONSTRAINT pk_train_student_checkin PRIMARY KEY (checkin_id) ); COMMENT ON TABLE public.train_student_checkin IS '学员课次考勤打卡记录表'; -- 20. 实际学习学时记录表 (主键 dur_id) CREATE TABLE IF NOT EXISTS public.train_study_duration ( dur_id BIGINT GENERATED BY DEFAULT AS IDENTITY, enrollment_id BIGINT NOT NULL, plan_course_id BIGINT, resource_id VARCHAR(32), learning_content VARCHAR(200), source_system VARCHAR(100), source_event_id VARCHAR(100), start_time TIMESTAMP, end_time TIMESTAMP, study_seconds INTEGER NOT NULL DEFAULT 0, sync_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, create_by VARCHAR(64), create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, remark VARCHAR(500), CONSTRAINT pk_train_study_duration PRIMARY KEY (dur_id), CONSTRAINT ck_study_seconds CHECK (study_seconds >= 0) ); COMMENT ON TABLE public.train_study_duration IS '实际学习学时记录表'; -- 21. 学员培训画像统计表 CREATE TABLE IF NOT EXISTS public.train_student_portrait ( portrait_id BIGINT GENERATED BY DEFAULT AS IDENTITY, enrollment_id BIGINT NOT NULL, attendance_rate NUMERIC(5,2), theory_score NUMERIC(7,2), practical_score NUMERIC(7,2), total_study_hour NUMERIC(8,2), label_tag TEXT, portrait_content TEXT, generated_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, create_by VARCHAR(64), create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, update_by VARCHAR(64), update_time TIMESTAMP, remark VARCHAR(500), CONSTRAINT pk_train_student_portrait PRIMARY KEY (portrait_id) ); COMMENT ON TABLE public.train_student_portrait IS '学员培训画像综合统计表'; -- ============================================================================== -- 第四部分: 外部资源对接与操作审计表 (2 张) -- ============================================================================== -- 22. 外部资源映射关联表 (DMS 适配支持) CREATE TABLE IF NOT EXISTS public.train_external_resource_mapping ( mapping_id BIGINT GENERATED BY DEFAULT AS IDENTITY, dorm_id VARCHAR(32) NOT NULL, vendor_code VARCHAR(32) NOT NULL, local_resource_type VARCHAR(32) NOT NULL, external_resource_type VARCHAR(32) NOT NULL, external_resource_id VARCHAR(64) NOT NULL, external_parent_id VARCHAR(64), external_hardware_id VARCHAR(64), external_project_id VARCHAR(64), is_primary SMALLINT NOT NULL DEFAULT 1, mapping_status VARCHAR(32) NOT NULL DEFAULT 'ACTIVE', last_sync_time TIMESTAMP, create_by VARCHAR(64), create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, update_by VARCHAR(64), update_time TIMESTAMP, remark VARCHAR(500), CONSTRAINT pk_train_ext_res_map PRIMARY KEY (mapping_id) ); COMMENT ON TABLE public.train_external_resource_mapping IS '外部资源映射关联表'; -- 23. 外部对接操作流水与补偿表 (列名 door_auth_id, business_type 严格对齐 Mapper) CREATE TABLE IF NOT EXISTS public.train_external_operation ( operation_id BIGINT GENERATED BY DEFAULT AS IDENTITY, door_auth_id BIGINT, business_type VARCHAR(32) NOT NULL, vendor_code VARCHAR(32) NOT NULL, operation_type VARCHAR(32) NOT NULL, operation_status VARCHAR(32) NOT NULL DEFAULT 'PENDING', idempotency_key VARCHAR(64) NOT NULL, external_operation_id VARCHAR(64), external_request_type VARCHAR(64), external_hardware_id VARCHAR(64), external_key_id VARCHAR(64), attempt_count INTEGER NOT NULL DEFAULT 0, next_poll_time TIMESTAMP, requested_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, completed_at TIMESTAMP, last_error_code VARCHAR(64), last_error_message VARCHAR(500), create_by VARCHAR(64), create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, update_by VARCHAR(64), update_time TIMESTAMP, remark VARCHAR(500), CONSTRAINT pk_train_external_operation PRIMARY KEY (operation_id) ); COMMENT ON TABLE public.train_external_operation IS '外部对接操作流水与补偿表'; COMMENT ON COLUMN public.train_external_operation.door_auth_id IS '门锁授权ID (对齐 TrainExternalOperationMapper)'; COMMENT ON COLUMN public.train_external_operation.business_type IS '业务类型 (对齐 TrainExternalOperationMapper)'; -- ============================================================================== -- 第五部分: 唯一约束 (UNIQUE) -- ============================================================================== ALTER TABLE public.train_student ADD CONSTRAINT uq_student_idcard_hash UNIQUE (id_card_hash); CREATE UNIQUE INDEX IF NOT EXISTS uq_student_person_no ON public.train_student (person_no) WHERE person_no IS NOT NULL AND del_flag = '0'; CREATE UNIQUE INDEX IF NOT EXISTS uq_teacher_person_no ON public.train_teacher (person_no) WHERE person_no IS NOT NULL AND del_flag = '0'; ALTER TABLE public.train_course ADD CONSTRAINT uq_course_code UNIQUE (c_code); ALTER TABLE public.train_electronic_board ADD CONSTRAINT uq_board_sn UNIQUE (board_sn); ALTER TABLE public.train_dormitory_info ADD CONSTRAINT uq_dorm_building_room UNIQUE (dorm_building, dorm_room); ALTER TABLE public.train_course_resource ADD CONSTRAINT uq_course_resource UNIQUE (c_id, resource_id); ALTER TABLE public.train_plan_course ADD CONSTRAINT uq_plan_course UNIQUE (plan_id, c_id); ALTER TABLE public.train_enrollment ADD CONSTRAINT uq_enrollment UNIQUE (plan_id, student_id); -- 【核心修复】废弃硬性全局 UNIQUE(enrollment_id),使用部分唯一索引保障:同一参训档案至多拥有1条激活中住宿(0预分配或1入住),同时支持历史退宿与重新入住 CREATE UNIQUE INDEX IF NOT EXISTS uq_accommodation_active_enroll ON public.train_accommodation (enrollment_id) WHERE accommodation_status IN (0, 1); ALTER TABLE public.train_dorm_auth ADD CONSTRAINT uq_dorm_auth_accomm UNIQUE (accommodation_id); ALTER TABLE public.train_class_report ADD CONSTRAINT uq_class_report_enroll UNIQUE (enrollment_id); ALTER TABLE public.train_student_checkin ADD CONSTRAINT uq_student_checkin_enroll_lesson UNIQUE (enrollment_id, lesson_id); CREATE UNIQUE INDEX IF NOT EXISTS uq_study_duration_idempotent ON public.train_study_duration (source_system, source_event_id) WHERE source_system IS NOT NULL AND source_event_id IS NOT NULL; ALTER TABLE public.train_student_portrait ADD CONSTRAINT uq_student_portrait_enroll UNIQUE (enrollment_id); CREATE UNIQUE INDEX IF NOT EXISTS uq_ext_res_map_vendor_dorm ON public.train_external_resource_mapping (vendor_code, dorm_id); CREATE UNIQUE INDEX IF NOT EXISTS uq_ext_op_idempotency ON public.train_external_operation (idempotency_key); -- ============================================================================== -- 第六部分: 外键约束 (FOREIGN KEY - ON DELETE NO ACTION) -- ============================================================================== ALTER TABLE public.train_course_resource ADD CONSTRAINT fk_cr_course FOREIGN KEY (c_id) REFERENCES public.train_course(c_id) ON DELETE NO ACTION; ALTER TABLE public.train_course_resource ADD CONSTRAINT fk_cr_resource FOREIGN KEY (resource_id) REFERENCES public.train_resource(resource_id) ON DELETE NO ACTION; ALTER TABLE public.train_plan_course ADD CONSTRAINT fk_pc_plan FOREIGN KEY (plan_id) REFERENCES public.train_class_plan(plan_id) ON DELETE NO ACTION; ALTER TABLE public.train_plan_course ADD CONSTRAINT fk_pc_course FOREIGN KEY (c_id) REFERENCES public.train_course(c_id) ON DELETE NO ACTION; ALTER TABLE public.train_enrollment ADD CONSTRAINT fk_enroll_plan FOREIGN KEY (plan_id) REFERENCES public.train_class_plan(plan_id) ON DELETE NO ACTION; ALTER TABLE public.train_enrollment ADD CONSTRAINT fk_enroll_student FOREIGN KEY (student_id) REFERENCES public.train_student(s_id) ON DELETE NO ACTION; ALTER TABLE public.train_accommodation ADD CONSTRAINT fk_accomm_enroll FOREIGN KEY (enrollment_id) REFERENCES public.train_enrollment(enrollment_id) ON DELETE NO ACTION; ALTER TABLE public.train_accommodation ADD CONSTRAINT fk_accomm_dorm FOREIGN KEY (dorm_id) REFERENCES public.train_dormitory_info(dorm_id) ON DELETE NO ACTION; ALTER TABLE public.train_lesson ADD CONSTRAINT fk_lesson_pc FOREIGN KEY (plan_course_id) REFERENCES public.train_plan_course(plan_course_id) ON DELETE NO ACTION; ALTER TABLE public.train_lesson ADD CONSTRAINT fk_lesson_teacher FOREIGN KEY (t_id) REFERENCES public.train_teacher(t_id) ON DELETE NO ACTION; ALTER TABLE public.train_lesson ADD CONSTRAINT fk_lesson_classroom FOREIGN KEY (class_room_id) REFERENCES public.train_classroom_info(class_room_id) ON DELETE NO ACTION; ALTER TABLE public.train_lesson ADD CONSTRAINT fk_lesson_site FOREIGN KEY (site_id) REFERENCES public.train_training_site(site_id) ON DELETE NO ACTION; ALTER TABLE public.train_score_item ADD CONSTRAINT fk_si_plan FOREIGN KEY (plan_id) REFERENCES public.train_class_plan(plan_id) ON DELETE NO ACTION; ALTER TABLE public.train_student_score ADD CONSTRAINT fk_ss_item FOREIGN KEY (score_item_id) REFERENCES public.train_score_item(score_item_id) ON DELETE NO ACTION; ALTER TABLE public.train_student_score ADD CONSTRAINT fk_ss_enroll FOREIGN KEY (enrollment_id) REFERENCES public.train_enrollment(enrollment_id) ON DELETE NO ACTION; ALTER TABLE public.train_dorm_auth ADD CONSTRAINT fk_da_accomm FOREIGN KEY (accommodation_id) REFERENCES public.train_accommodation(accommodation_id) ON DELETE NO ACTION; ALTER TABLE public.train_class_report ADD CONSTRAINT fk_cr_enroll FOREIGN KEY (enrollment_id) REFERENCES public.train_enrollment(enrollment_id) ON DELETE NO ACTION; ALTER TABLE public.train_student_checkin ADD CONSTRAINT fk_sci_enroll FOREIGN KEY (enrollment_id) REFERENCES public.train_enrollment(enrollment_id) ON DELETE NO ACTION; ALTER TABLE public.train_student_checkin ADD CONSTRAINT fk_sci_lesson FOREIGN KEY (lesson_id) REFERENCES public.train_lesson(lesson_id) ON DELETE NO ACTION; ALTER TABLE public.train_study_duration ADD CONSTRAINT fk_sd_enroll FOREIGN KEY (enrollment_id) REFERENCES public.train_enrollment(enrollment_id) ON DELETE NO ACTION; ALTER TABLE public.train_student_portrait ADD CONSTRAINT fk_sp_enroll FOREIGN KEY (enrollment_id) REFERENCES public.train_enrollment(enrollment_id) ON DELETE NO ACTION; ALTER TABLE public.train_external_resource_mapping ADD CONSTRAINT fk_erm_dorm FOREIGN KEY (dorm_id) REFERENCES public.train_dormitory_info(dorm_id) ON DELETE NO ACTION; -- ============================================================================== -- 第七部分: 业务索引 (INDEXES) -- ============================================================================== CREATE INDEX IF NOT EXISTS idx_teacher_user_id ON public.train_teacher (user_id); CREATE INDEX IF NOT EXISTS idx_teacher_person_no ON public.train_teacher (person_no); CREATE INDEX IF NOT EXISTS idx_student_user_id ON public.train_student (user_id); CREATE INDEX IF NOT EXISTS idx_student_person_no ON public.train_student (person_no); CREATE INDEX IF NOT EXISTS idx_course_status ON public.train_course (status); CREATE INDEX IF NOT EXISTS idx_dorm_gender ON public.train_dormitory_info (gender_type, dorm_status); CREATE INDEX IF NOT EXISTS idx_dorm_lock_sn ON public.train_dormitory_info (lock_device_sn); CREATE INDEX IF NOT EXISTS idx_class_plan_date ON public.train_class_plan (start_time, end_time); CREATE INDEX IF NOT EXISTS idx_class_plan_status ON public.train_class_plan (plan_status); CREATE INDEX IF NOT EXISTS idx_enrollment_plan ON public.train_enrollment (plan_id, enrollment_status); CREATE INDEX IF NOT EXISTS idx_accommodation_dorm ON public.train_accommodation (dorm_id, accommodation_status); CREATE INDEX IF NOT EXISTS idx_lesson_pc ON public.train_lesson (plan_course_id); CREATE INDEX IF NOT EXISTS idx_lesson_teacher ON public.train_lesson (t_id); CREATE INDEX IF NOT EXISTS idx_score_item_plan ON public.train_score_item (plan_id); CREATE INDEX IF NOT EXISTS idx_student_score_enroll ON public.train_student_score (enrollment_id); CREATE INDEX IF NOT EXISTS idx_dorm_auth_sync ON public.train_dorm_auth (face_sync_status, auth_status); COMMIT;