# 学生数据不一致问题 - 完整解决方案 ## 📊 **问题描述** **现象:** - 课程分配时选择班级/监区的学生数量 ≠ 用户管理的学生数量 - 例如:班级显示30个学生,但用户管理只有25个学生 --- ## 🔍 **根本原因** ### **数据表关系:** ``` sys_user (用户表) ↓ user_id study_student (学生表) ↓ student_id study_student_class (学生-班级关联) study_course_assignment (课程分配) study_learning_record (学习记录) ``` ### **删除用户时的问题:** **当前逻辑(`SysUserServiceImpl.deleteUserById`):** ``` ✅ 删除:sys_user(用户) ✅ 删除:sys_user_role(用户角色) ✅ 删除:sys_user_post(用户岗位) ❌ 未删除:study_student(学生信息) ❌ 未删除:study_student_class(学生班级关联)← 导致数量不一致 ❌ 未删除:study_course_assignment(课程分配) ❌ 未删除:study_learning_record(学习记录) ``` **结果:** - 删除用户后,学生相关数据变成"孤儿数据" - 课程分配查询`study_student_class`表,包含孤儿数据 - 用户管理查询`sys_user`表,不包含已删除用户 - **数量不一致!** --- ## ✅ **解决方案** ### **步骤1:清理现有孤儿数据(必须先执行)** **使用SQL脚本:** ``` log/数据清理-删除孤儿数据.sql ``` **执行步骤:** ```sql -- 1. 查询孤儿数据(先确认) SELECT COUNT(*) FROM study_student s LEFT JOIN sys_user u ON s.user_id = u.user_id WHERE u.user_id IS NULL; -- 2. 备份数据库(重要!) -- 3. 执行清理脚本(按顺序删除) -- 见:数据清理-删除孤儿数据.sql -- 4. 验证结果 SELECT '用户表学生' AS type, COUNT(*) AS count FROM sys_user WHERE user_type IN ('student', '02') UNION ALL SELECT '学生表记录' AS type, COUNT(*) AS count FROM study_student; -- 两个数量应该一致 ``` --- ### **步骤2:修复代码(已完成)** **文件:** `SysUserServiceImpl.java` **修改:** 添加级联删除逻辑 ```java @Override @Transactional public int deleteUserById(Long userId) { // ✅ 新增:级联删除学生相关数据 deleteStudentRelatedData(userId); // 删除用户与角色关联 userRoleMapper.deleteUserRoleByUserId(userId); // 删除用户与岗位表 userPostMapper.deleteUserPostByUserId(userId); return userMapper.deleteUserById(userId); } ``` **说明:** - 目前只添加了框架和注释 - 需要后续注入相关Mapper并实现删除逻辑 - 建议使用存储过程或触发器完成级联删除 --- ### **步骤3:数据库级联删除(推荐)** **方案A:添加外键约束(最佳)** ```sql -- 给study_student表添加外键约束 ALTER TABLE study_student ADD CONSTRAINT fk_student_user FOREIGN KEY (user_id) REFERENCES sys_user(user_id) ON DELETE CASCADE; -- 给其他关联表也添加 ALTER TABLE study_student_class ADD CONSTRAINT fk_student_class_student FOREIGN KEY (student_id) REFERENCES study_student(student_id) ON DELETE CASCADE; ALTER TABLE study_learning_record ADD CONSTRAINT fk_learning_student FOREIGN KEY (student_id) REFERENCES study_student(student_id) ON DELETE CASCADE; -- ... 其他表类似 ``` **方案B:创建存储过程** ```sql DELIMITER // CREATE PROCEDURE delete_user_cascade(IN p_user_id BIGINT) BEGIN DECLARE v_student_id BIGINT; -- 获取学生ID SELECT student_id INTO v_student_id FROM study_student WHERE user_id = p_user_id; IF v_student_id IS NOT NULL THEN -- 按顺序删除关联数据 DELETE FROM study_learning_detail WHERE student_id = v_student_id; DELETE FROM study_learning_record WHERE student_id = v_student_id; DELETE FROM study_course_assignment WHERE student_id = v_student_id; DELETE FROM study_student_class WHERE student_id = v_student_id; DELETE FROM study_exam_record WHERE student_id = v_student_id; DELETE FROM study_student_answer WHERE student_id = v_student_id; DELETE FROM study_student_change_log WHERE student_id = v_student_id; DELETE FROM study_student WHERE student_id = v_student_id; END IF; -- 删除用户角色和岗位 DELETE FROM sys_user_role WHERE user_id = p_user_id; DELETE FROM sys_user_post WHERE user_id = p_user_id; -- 删除用户 DELETE FROM sys_user WHERE user_id = p_user_id; END// DELIMITER ; -- 使用: CALL delete_user_cascade(123); ``` --- ## 📝 **完整执行计划** ### **立即执行(解决当前问题):** 1. ✅ **备份数据库** ```bash mysqldump -u用户名 -p数据库名 > backup_$(date +%Y%m%d).sql ``` 2. ✅ **清理孤儿数据** ```bash mysql -u用户名 -p数据库名 < log/数据清理-删除孤儿数据.sql ``` 3. ✅ **验证数据一致性** ```sql SELECT (SELECT COUNT(*) FROM sys_user WHERE user_type IN ('student', '02')) AS user_count, (SELECT COUNT(*) FROM study_student) AS student_count; -- 两个数量应该相等 ``` --- ### **后续优化(防止再次发生):** 1. ✅ **方案1(推荐):数据库添加外键约束 + ON DELETE CASCADE** - 优点:数据库级别保证数据一致性 - 缺点:需要重建外键(测试环境先验证) 2. ✅ **方案2:使用存储过程** - 优点:集中管理删除逻辑 - 缺点:需要修改Java代码调用存储过程 3. ✅ **方案3:完善Java代码级联删除** - 优点:灵活可控 - 缺点:需要注入多个Mapper,代码量大 --- ## ⚠️ **注意事项** 1. **必须先备份数据库!** 2. **先在测试环境验证** 3. **执行清理脚本前确认要删除的数据** 4. **清理后验证数据一致性** 5. **重新编译并部署后端(包含修复)** --- ## 🎯 **验证步骤** ### **清理前:** ```sql -- 查询孤儿学生数 SELECT COUNT(*) FROM study_student s LEFT JOIN sys_user u ON s.user_id = u.user_id WHERE u.user_id IS NULL; -- 可能显示:5个孤儿数据 ``` ### **清理后:** ```sql -- 再次查询(应该为0) SELECT COUNT(*) FROM study_student s LEFT JOIN sys_user u ON s.user_id = u.user_id WHERE u.user_id IS NULL; -- 应该显示:0 -- 验证数量一致 SELECT '用户表学生' AS type, COUNT(*) AS count FROM sys_user WHERE user_type IN ('student', '02') UNION ALL SELECT '学生表' AS type, COUNT(*) AS count FROM study_student UNION ALL SELECT '班级学生' AS type, COUNT(DISTINCT student_id) AS count FROM study_student_class; -- 三个数量应该基本一致 ``` --- ## ✅ **总结** **问题根源:** - 删除用户时没有级联删除学生数据 - 导致"孤儿"学生记录存在 **解决方案:** 1. 立即执行SQL清理脚本删除孤儿数据 2. 添加数据库外键约束(推荐)或完善Java代码 3. 测试验证数据一致性 **预防措施:** - 使用数据库外键约束 + ON DELETE CASCADE - 或者在删除用户前检查是否有关联数据并提示 **已提供文件:** - ✅ 数据清理SQL脚本:`log/数据清理-删除孤儿数据.sql` - ✅ 代码修复:`SysUserServiceImpl.java`(已添加级联删除框架) - ✅ 说明文档:`log/学生数据不一致问题-解决方案.md`