7.1 KiB
7.1 KiB
学生数据不一致问题 - 完整解决方案
📊 问题描述
现象:
- 课程分配时选择班级/监区的学生数量 ≠ 用户管理的学生数量
- 例如:班级显示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
执行步骤:
-- 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
修改: 添加级联删除逻辑
@Override
@Transactional
public int deleteUserById(Long userId)
{
// ✅ 新增:级联删除学生相关数据
deleteStudentRelatedData(userId);
// 删除用户与角色关联
userRoleMapper.deleteUserRoleByUserId(userId);
// 删除用户与岗位表
userPostMapper.deleteUserPostByUserId(userId);
return userMapper.deleteUserById(userId);
}
说明:
- 目前只添加了框架和注释
- 需要后续注入相关Mapper并实现删除逻辑
- 建议使用存储过程或触发器完成级联删除
步骤3:数据库级联删除(推荐)
方案A:添加外键约束(最佳)
-- 给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:创建存储过程
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);
📝 完整执行计划
立即执行(解决当前问题):
-
✅ 备份数据库
mysqldump -u用户名 -p数据库名 > backup_$(date +%Y%m%d).sql -
✅ 清理孤儿数据
mysql -u用户名 -p数据库名 < log/数据清理-删除孤儿数据.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(推荐):数据库添加外键约束 + ON DELETE CASCADE
- 优点:数据库级别保证数据一致性
- 缺点:需要重建外键(测试环境先验证)
-
✅ 方案2:使用存储过程
- 优点:集中管理删除逻辑
- 缺点:需要修改Java代码调用存储过程
-
✅ 方案3:完善Java代码级联删除
- 优点:灵活可控
- 缺点:需要注入多个Mapper,代码量大
⚠️ 注意事项
- 必须先备份数据库!
- 先在测试环境验证
- 执行清理脚本前确认要删除的数据
- 清理后验证数据一致性
- 重新编译并部署后端(包含修复)
🎯 验证步骤
清理前:
-- 查询孤儿学生数
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个孤儿数据
清理后:
-- 再次查询(应该为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;
-- 三个数量应该基本一致
✅ 总结
问题根源:
- 删除用户时没有级联删除学生数据
- 导致"孤儿"学生记录存在
解决方案:
- 立即执行SQL清理脚本删除孤儿数据
- 添加数据库外键约束(推荐)或完善Java代码
- 测试验证数据一致性
预防措施:
- 使用数据库外键约束 + ON DELETE CASCADE
- 或者在删除用户前检查是否有关联数据并提示
已提供文件:
- ✅ 数据清理SQL脚本:
log/数据清理-删除孤儿数据.sql - ✅ 代码修复:
SysUserServiceImpl.java(已添加级联删除框架) - ✅ 说明文档:
log/学生数据不一致问题-解决方案.md